2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
5 Desc: AROS-specific function for adding a display driver
9 #include <aros/debug.h>
10 #include <graphics/driver.h>
11 #include <hidd/compositor.h>
14 #include <proto/utility.h>
16 #include "graphics_intern.h"
17 #include "compositor_driver.h"
20 #define CL(x) ((OOP_Class *)x)
21 #define IS_CLASS(x, n) (CL(x)->ClassNode.ln_Name && (!strcmp(CL(x)->ClassNode.ln_Name, n)))
23 /*****************************************************************************
26 #include <proto/graphics.h>
28 AROS_LH3(ULONG
, AddDisplayDriverA
,
31 AROS_LHA(APTR
, gfxclass
, A0
),
32 AROS_LHA(struct TagItem
*, attrs
, A1
),
33 AROS_LHA(struct TagItem
*, tags
, A2
),
36 struct GfxBase
*, GfxBase
, 107, Graphics
)
39 Add a display driver to the system.
42 gfxhidd - A pointer to an OOP class of the display driver
43 attrs - Additional attributes to supply to the driver class during
45 tags - An optional TagList describing how graphics.library should
46 handle the driver. Valid tags are:
48 DDRV_BootMode - A boolean value telling that a boot mode
49 driver is being added. Boot mode drivers
50 will automatically shut down on next
51 AddDisplayDriverA() call, unless
52 DDRV_KeepBootMode = TRUE is specified.
54 DDRV_MonitorID - Starting monitor ID to assign to the driver.
55 Use it with care. An attempt to add already
56 existing ID will fail with a DD_ID_EXISTS
57 code. By default the next available ID will
58 be picked up automatically.
59 DDRV_ReserveIDs - The number of subsequent monitor IDs to
60 reserve. Reserved IDs can be reused only
61 with DDRV_MonitorID tag. This tag is
62 provided as an aid to support possible
63 removable display devices. Defaults to 1.
64 DDRV_KeepBootMode - Do not shut down boot mode drivers. Use this
65 tag if you are 100% sure that your driver
66 won't conflict with boot mode driver (like
67 VGA or VESA) and won't attempt to take over
68 its hardware. Defaults to FALSE.
69 DDRV_ResultID - A pointer to a ULONG location where the ID
70 assigned to your driver will be placed.
71 Useful if you reserve some ID for future use.
72 Note that the returned ID will be the one
73 just assigned to your driver instance.
74 Increment it yourself in order to obtain
76 DDRV_IDMask - A mask for separating the monitor ID from the
77 HIDD-specific part. This mask specifies what
78 mode ID bits are the monitor ID and what bits
79 actually specify the mode. The default value
82 Using the mask you can split your monitor ID
83 into 'sub-Ids'. Example:
85 Supplied tags: DDRV_IDMask, 0xFFFFFF00,
88 After a successful call, myid will contain the
89 base ID assigned by graphics.library to your
90 driver, let's say 0x00140000. However, since
91 you specified a longer mask, you leave only
92 one byte for mode designation, and reserve
93 the whole range of IDs from 0x001400xx to
94 0x0014FFxx for different instances of your
95 driver. They can now be used by specifying
96 DDRV_MonitorID with corresponding value.
98 Note that for this feature to work correctly,
99 you also need to override ID processing in
100 your driver class. Default methods provided
101 by the hidd.graphics.graphics base class
102 suppose that the whole lower word of the mode
103 ID specifies the display mode.
105 It is generally not allowed to specify
106 shorter masks than 0xFFFF0000. The only
107 driver which can do this is the Amiga(TM)
108 chipset driver, which needs to occupy the
109 reserved range of IDs from 0x0000xxxx to
110 0x000Axxxx. In any other case, supplying a
111 short mask will cause undefined behavior.
113 Since DDRV_ReserveIDs provides a simpler way
114 to reserve IDs for your driver (without the
115 need to override mode ID processing), this
116 option can be considered experimental and
117 even private. In fact the primary reason for
118 it to exist is to provide support for
119 Amiga(tm) chipset driver.
122 error - One of following codes:
124 DD_OK - Operation completed OK.
125 DD_NO_MEM - There is not enough memory to set up internal
127 DD_ID_EXISTS - Attempt to assign monitor IDs that are already
129 DD_IN_USE - One of boot-mode drivers is in use and cannot
131 DD_DRIVER_ERROR - Failure to create driver object.
134 This function is AROS-specific.
144 *****************************************************************************/
148 struct TagItem
*tag
, *tstate
= (struct TagItem
*)tags
;
149 struct monitor_driverdata
*mdd
;
150 ULONG FirstID
= INVALID_ID
;
153 ULONG IDMask
= AROS_MONITOR_ID_MASK
;
154 BOOL keep_boot
= FALSE
;
156 ULONG
*ResultID
= NULL
;
159 EnterFunc(bug("AddDisplayDriverA(0x%p) <%s>\n", gfxclass
, CL(gfxclass
)->ClassNode
.ln_Name
));
162 * MAGIC: Detect composition HIDD here.
163 * This allows to hotplug it, and even (potentially) replace.
165 if (IS_CLASS(gfxclass
, CLID_Hidd_Compositor
))
167 ObtainSemaphore(&CDD(GfxBase
)->displaydb_sem
);
168 ret
= compositor_Install(gfxclass
, GfxBase
);
169 ReleaseSemaphore(&CDD(GfxBase
)->displaydb_sem
);
174 /* First parse parameters */
175 while ((tag
= NextTagItem(&tstate
)))
180 FirstID
= tag
->ti_Data
;
183 case DDRV_ReserveIDs
:
184 NumIDs
= tag
->ti_Data
;
188 IDMask
= tag
->ti_Data
;
191 case DDRV_KeepBootMode
:
192 keep_boot
= tag
->ti_Data
;
196 flags
= tag
->ti_Data
? DF_BootMode
: 0;
200 ResultID
= (ULONG
*)tag
->ti_Data
;
205 /* We lock for the entire function because we want to be sure that
206 IDs will remain free during driver_Setup() */
207 ObtainSemaphore(&CDD(GfxBase
)->displaydb_sem
);
209 /* Default value for monitor ID */
210 if (FirstID
== INVALID_ID
)
213 * The logic here prevents ID clash if specified mask is wider than previous one.
215 * 1. Add driver with mask = 0xFFFF0000. FirstID = 0x00100000, NextID = 0x00110000.
216 * 2. Add driver with mask = 0xF0000000. FirstID = 0x00110000, AND with this mask
217 * would give monitor ID = 0.
218 * In order to prevent this, we make one more increment, so that in (2) FirstID becomes
219 * 0x10000000. The increment mechanism itself is explained below.
220 * Note that the adjustments happens only for automatic ID assignment. In case of manual
221 * one (DDRV_MonitorID specified) we suggest our caller knows what he does.
223 FirstID
= CDD(GfxBase
)->last_id
& IDMask
;
225 if (FirstID
< CDD(GfxBase
)->last_id
)
226 FirstID
+= (~(IDMask
& AROS_MONITOR_ID_MASK
) + 1);
230 * Calculate next free ID.
231 * Mechanism of increment calculation: we invert the mask and add 1 to it.
232 * This way for example 0xFFFF0000 becomes 0x00010000.
233 * Before doing this we make sure that mask used in this equation is not
234 * longer than 0xFFFF0000, for proper ID counting.
236 NextID
= FirstID
+ NumIDs
* (~(IDMask
& AROS_MONITOR_ID_MASK
) + 1);
237 D(bug("[AddDisplayDriverA] First ID 0x%08X, next ID 0x%08X\n", FirstID
, NextID
));
239 /* First check if the operation can actually be performed */
240 for (mdd
= CDD(GfxBase
)->monitors
; mdd
; mdd
= mdd
->next
)
242 /* Check if requested IDs are already allocated */
243 if ((mdd
->id
>= FirstID
&& mdd
->id
< NextID
))
250 * Now check if boot mode drivers can really be unloaded.
251 * Display drivers can start playing with their hardware during
252 * object creation, so we need to check it before instantiating
257 /* The driver can be unloaded if it has nothing on display */
258 if ((mdd
->flags
& DF_BootMode
) && (mdd
->display
))
267 * Now, if everything is okay, we are ready to instantiate the driver.
268 * A well-behaved driver must touch the hardware only in object, not
269 * in class. This makes this function much safer. If we can't exit boot mode,
270 * the driver will not be instantiated and hardware state will not be clobbered.
274 OOP_Object
*gfxhidd
= OOP_NewObject(gfxclass
, NULL
, attrs
);
278 D(bug("[AddDisplayDriverA] Installing driver\n"));
280 /* Attach system structures to the driver */
281 mdd
= driver_Setup(gfxhidd
, GfxBase
);
282 D(bug("[AddDisplayDriverA] monitor_driverdata 0x%p\n", mdd
));
286 struct monitor_driverdata
*last
, *old
;
292 if (CDD(GfxBase
)->DriverNotify
)
294 /* Use mdd->gfxhidd here because it can be substituted by fakegfx object */
295 mdd
->userdata
= CDD(GfxBase
)->DriverNotify(mdd
, TRUE
, CDD(GfxBase
)->notify_data
);
298 /* Remove boot mode drivers */
301 D(bug("[AddDisplayDriverA] Shutting down boot mode drivers\n"));
302 for (last
= (struct monitor_driverdata
*)CDD(GfxBase
);; last
= last
->next
)
304 D(bug("[AddDisplayDriverA] Current 0x%p, next 0x%p\n", last
, last
->next
));
306 while (last
->next
&& (last
->next
->flags
& DF_BootMode
))
309 D(bug("[AddDisplayDriverA] Shutting down driver 0x%p (ID 0x%08lX, next 0x%p)\n", old
, old
->id
, old
->next
));
311 last
->next
= old
->next
;
312 driver_Expunge(old
, GfxBase
);
313 D(bug("[AddDisplayDriverA] Shutdown OK, next 0x%p\n", last
->next
));
317 * We check this condition here explicitly because last->next is modified inside loop body.
318 * If we check it in for() statement, last = last->next will be executed BEFORE the check,
319 * and NULL pointer may be hit.
326 /* Insert the driverdata into chain, sorted by ID */
327 D(bug("[AddDisplayDriverA] Inserting driver 0x%p, ID 0x%08lX\n", mdd
, mdd
->id
));
328 for (last
= (struct monitor_driverdata
*)CDD(GfxBase
); last
->next
; last
= last
->next
)
330 D(bug("[AddDisplayDriverA] Current 0x%p, next 0x%p, ID 0x%08lX\n", last
, last
->next
, last
->next
->id
));
331 if (mdd
->id
< last
->next
->id
)
335 D(bug("[AddDisplayDriverA] Inserting after 0x%p\n", last
));
336 mdd
->next
= last
->next
;
339 /* Remember next available ID */
340 if (NextID
> CDD(GfxBase
)->last_id
)
341 CDD(GfxBase
)->last_id
= NextID
;
343 /* Return the assigned ID if the caller asked to do so */
349 OOP_DisposeObject(gfxhidd
);
353 else /* if (gfxhidd) */
355 ret
= DD_DRIVER_ERROR
;
357 } /* if (ret == DD_OK) */
359 ReleaseSemaphore(&CDD(GfxBase
)->displaydb_sem
);
361 /* Set the first non-boot non-planar driver as default */
362 if ((ret
== DD_OK
) && (!GfxBase
->default_monitor
) && (!(mdd
->flags
& DF_BootMode
)))
365 * Amiga(tm) chipset driver does not become a default.
366 * This is done because RTG modes (if any) are commonly preferred
368 * TODO: in future some prefs program could be implemented. It would
369 * allow the user to describe the physical placement of several displays
370 * in his environment, and explicitly set the preferred display.
372 if (!IS_CLASS(gfxclass
, "hidd.gfx.amigavideo"))
375 * graphics.library uses struct MonitorSpec pointers for historical reasons,
377 * Here we just get the first available sync object from the driver and
378 * set default_monitor fo its MonitorSpec. This allows BestModeIDA() to
379 * obtain preferred monitor back from this MonitorSpec (by asking the associated
380 * sync object about its parent driver).
383 * Originally display drivers in AmigaOS had a concept of "preferred mode ID".
384 * Every driver supplied own hardcoded ID which can be retrieved by GetDisplayInfoData()
385 * in MonitorInfo->PreferredModeID. Currently AROS does not implement this concept.
386 * However this sync could be a preferred mode's sync.
387 * It needs to be researched what exactly this mode ID is. Implementing this concept would
388 * improve AmigaOS(tm) compatibility.
390 OOP_Object
*sync
= HIDD_Gfx_GetSync(mdd
->gfxhidd_orig
, 0);
392 OOP_GetAttr(sync
, aHidd_Sync_MonitorSpec
, (IPTR
*)&GfxBase
->default_monitor
);
396 D(bug("[AddDisplayDriverA] Returning %u\n", ret
));