Added more controller IDs.
[AROS.git] / rom / graphics / adddisplaydrivera.c
blob8f5329a734085513cdfec36ab91c47b8836e67e4
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: AROS-specific function for adding a display driver
6 Lang: english
7 */
9 #include <aros/debug.h>
10 #include <graphics/driver.h>
11 #include <hidd/compositing.h>
12 #include <oop/oop.h>
14 #include <proto/utility.h>
16 #include "graphics_intern.h"
17 #include "compositing_driver.h"
18 #include "dispinfo.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 /*****************************************************************************
25 NAME */
26 #include <proto/graphics.h>
28 AROS_LH3(ULONG, AddDisplayDriverA,
30 /* SYNOPSIS */
31 AROS_LHA(APTR, gfxclass, A0),
32 AROS_LHA(struct TagItem *, attrs, A1),
33 AROS_LHA(const struct TagItem *, tags, A2),
35 /* LOCATION */
36 struct GfxBase *, GfxBase, 107, Graphics)
38 /* FUNCTION
39 Add a display driver to the system.
41 INPUTS
42 gfxhidd - A pointer to an OOP class of the display driver
43 attrs - Additional attributes to supply to the driver class during object creation
44 tags - An optional TagList describing how graphics.library should handle the driver.
45 Valid tags are:
47 DDRV_BootMode - A boolean value telling that a boot mode driver
48 is being added. Boot mode drivers will automatically
49 shutdown on next AddDisplayDriverA() call, unless
50 DDRV_KeepBootMode = TRUE is specified. Defaults to FALSE.
51 DDRV_MonitorID - Starting monitor ID to assign to the driver. Use it
52 with care. Attempt to add already existing ID will
53 fail with DD_ID_EXISTS code. By default a next available
54 ID will be picked up automatically.
55 DDRV_ReserveIDs - A number of subsequent monitor IDs to reserve. Reserved IDs
56 can be reused only with DDRV_MonitorID tag. This tag is
57 provided as an aid to support possible removable display
58 devices. Defaults to 1.
59 DDRV_KeepBootMode - Do not shut down boot mode drivers. Use this tag if you
60 are 100% sure that your driver won't conflict with boot mode
61 driver (like VGA or VESA) and won't attempt to take over its
62 hardware. Defaults to FALSE.
63 DDRV_ResultID - A pointer to ULONG location where ID assigned to your driver
64 will be placed. Useful if you reserve some ID for future use.
65 Note that returned ID will be the one just assigned to your
66 driver instance. Increment it yourself in order to obtain
67 other reserved IDs.
68 DDRV_IDMask - A mask for separating monitor ID from HIDD-specific part.
69 This mask specifies what mode ID bits are monitor ID and
70 what bits actually specify the mode. A default value is
71 0xFFFF0000.
73 Using the mask you can split your monitor ID into 'sub-Ids'.
74 Example:
76 Supplied tags: DDRV_IDMask, 0xFFFFFF00, DDRV_ResultID, &myid
78 After succesfull call myid will contain base ID assigned by
79 graphics.library to your driver, let's say 0x00140000. However,
80 since you specified longer mask, you leave only one byte for mode
81 designation, and reserve the whole range of IDs from 0x001400xx to
82 0x0014FFxx for different instances of your driver. They can now be
83 used by specifying DDRV_MonitorID with corresponding value.
85 Note that for this feature to work correctly, you also need to override
86 mode ID processing in your driver class. Default methods provided by
87 hidd.graphics.graphics base class suppose that the whole lower word
88 of mode ID specifies the display mode.
90 It is generally not allowed to specify shorter masks than 0xFFFF0000.
91 The only driver which can do this is Amiga(tm) chipset driver, which
92 need to occupy the reserved range of IDs from 0x0000xxxx to 0x000Axxxx.
93 In any other case supplying short mask will cause undefined behavior.
95 Since DDRV_ReserveIDs provide simpler way to reserve IDs for your driver
96 (without the need to override mode ID processing), this option can be
97 considered experimental and even private. In fact the primary reason for
98 it to exist is to provide support for Amiga(tm) chipset driver.
100 RESULT
101 error - One of following codes:
103 DD_OK - Operation completed OK.
104 DD_NO_MEM - There is not enough memory to set up internal data.
105 DD_ID_EXISTS - Attempt to assign monitor IDs that are already used.
106 DD_IN_USE - One of boot-mode drivers is in use and can not be shut down.
107 DD_DRIVER_ERROR - Failure to create driver object.
109 NOTES
110 This function is AROS-specific.
112 EXAMPLE
114 BUGS
116 SEE ALSO
118 INTERNALS
120 HISTORY
121 29-10-95 digulla automatically created from
122 graphics_lib.fd and clib/graphics_protos.h
124 *****************************************************************************/
126 AROS_LIBFUNC_INIT
128 struct TagItem *tag, *tstate = (struct TagItem *)tags;
129 struct monitor_driverdata *mdd;
130 ULONG FirstID = INVALID_ID;
131 ULONG NextID;
132 ULONG NumIDs = 1;
133 ULONG IDMask = AROS_MONITOR_ID_MASK;
134 BOOL keep_boot = FALSE;
135 UWORD flags = 0;
136 ULONG *ResultID = NULL;
137 ULONG ret = DD_OK;
139 EnterFunc(bug("AddDisplayDriverA(0x%p) <%s>\n", gfxclass, CL(gfxclass)->ClassNode.ln_Name));
142 * MAGIC: Detect composition HIDD here.
143 * This allows to hotplug it, and even (potentially) replace.
145 if (IS_CLASS(gfxclass, CLID_Hidd_Compositing))
147 ObtainSemaphore(&CDD(GfxBase)->displaydb_sem);
148 ret = composer_Install(gfxclass, GfxBase);
149 ReleaseSemaphore(&CDD(GfxBase)->displaydb_sem);
151 return ret;
154 /* First parse parameters */
155 while ((tag = NextTagItem(&tstate)))
157 switch (tag->ti_Tag)
159 case DDRV_MonitorID:
160 FirstID = tag->ti_Data;
161 break;
163 case DDRV_ReserveIDs:
164 NumIDs = tag->ti_Data;
165 break;
167 case DDRV_IDMask:
168 IDMask = tag->ti_Data;
169 break;
171 case DDRV_KeepBootMode:
172 keep_boot = tag->ti_Data;
173 break;
175 case DDRV_BootMode:
176 flags = tag->ti_Data ? DF_BootMode : 0;
177 break;
179 case DDRV_ResultID:
180 ResultID = (ULONG *)tag->ti_Data;
181 break;
185 /* We lock for the entire function because we want to be sure that
186 IDs will remain free during driver_Setup() */
187 ObtainSemaphore(&CDD(GfxBase)->displaydb_sem);
189 /* Default value for monitor ID */
190 if (FirstID == INVALID_ID)
193 * The logic here prevents ID clash if specified mask is wider than previous one.
194 * Example situation:
195 * 1. Add driver with mask = 0xFFFF0000. FirstID = 0x00100000, NextID = 0x00110000.
196 * 2. Add driver with mask = 0xF0000000. FirstID = 0x00110000, AND with this mask
197 * would give monitor ID = 0.
198 * In order to prevent this, we make one more increment, so that in (2) FirstID becomes
199 * 0x10000000. The increment mechanism itself is explained below.
200 * Note that the adjustments happens only for automatic ID assignment. In case of manual
201 * one (DDRV_MonitorID specified) we suggest our caller knows what he does.
203 FirstID = CDD(GfxBase)->last_id & IDMask;
205 if (FirstID < CDD(GfxBase)->last_id)
206 FirstID += (~(IDMask & AROS_MONITOR_ID_MASK) + 1);
210 * Calculate next free ID.
211 * Mechanism of increment calculation: we invert the mask and add 1 to it.
212 * This way for example 0xFFFF0000 becomes 0x00010000.
213 * Before doing this we make sure that mask used in this equation is not
214 * longer than 0xFFFF0000, for proper ID counting.
216 NextID = FirstID + NumIDs * (~(IDMask & AROS_MONITOR_ID_MASK) + 1);
217 D(bug("[AddDisplayDriverA] First ID 0x%08X, next ID 0x%08X\n", FirstID, NextID));
219 /* First check if the operation can actually be performed */
220 for (mdd = CDD(GfxBase)->monitors; mdd; mdd = mdd->next)
222 /* Check if requested IDs are already allocated */
223 if ((mdd->id >= FirstID && mdd->id < NextID))
225 ret = DD_ID_EXISTS;
226 break;
230 * Now check if boot mode drivers can really be unloaded.
231 * Display drivers can start playing with their hardware during
232 * object creation, so we need to check it before instantiating
233 * the given class.
235 if (!keep_boot)
237 /* The driver can be unloaded if it has nothing on display */
238 if ((mdd->flags & DF_BootMode) && (mdd->display))
240 ret = DD_IN_USE;
241 break;
247 * Now, if everything is okay, we are ready to instantiate the driver.
248 * A well-behaved driver must touch the hardware only in object, not
249 * in class. This makes this function much safer. If we can't exit boot mode,
250 * the driver will not be instantiated and hardware state will not be clobbered.
252 if (ret == DD_OK)
254 OOP_Object *gfxhidd = OOP_NewObject(gfxclass, NULL, attrs);
256 if (gfxhidd)
258 D(bug("[AddDisplayDriverA] Installing driver\n"));
260 /* Attach system structures to the driver */
261 mdd = driver_Setup(gfxhidd, GfxBase);
262 D(bug("[AddDisplayDriverA] monitor_driverdata 0x%p\n", mdd));
264 if (mdd)
266 struct monitor_driverdata *last, *old;
268 mdd->id = FirstID;
269 mdd->mask = IDMask;
270 mdd->flags |= flags;
272 if (CDD(GfxBase)->DriverNotify)
274 /* Use mdd->gfxhidd here because it can be substituted by fakegfx object */
275 mdd->userdata = CDD(GfxBase)->DriverNotify(mdd, TRUE, CDD(GfxBase)->notify_data);
278 /* Remove boot mode drivers */
279 if (!keep_boot)
281 D(bug("[AddDisplayDriverA] Shutting down boot mode drivers\n"));
282 for (last = (struct monitor_driverdata *)CDD(GfxBase);; last = last->next)
284 D(bug("[AddDisplayDriverA] Current 0x%p, next 0x%p\n", last, last->next));
286 while (last->next && (last->next->flags & DF_BootMode))
288 old = last->next;
289 D(bug("[AddDisplayDriverA] Shutting down driver 0x%p (ID 0x%08lX, next 0x%p)\n", old, old->id, old->next));
291 last->next = old->next;
292 driver_Expunge(old, GfxBase);
293 D(bug("[AddDisplayDriverA] Shutdown OK, next 0x%p\n", last->next));
297 * We check this condition here explicitly because last->next is modified inside loop body.
298 * If we check it in for() statement, last = last->next will be executed BEFORE the check,
299 * and NULL pointer may be hit.
301 if (!last->next)
302 break;
306 /* Insert the driverdata into chain, sorted by ID */
307 D(bug("[AddDisplayDriverA] Inserting driver 0x%p, ID 0x%08lX\n", mdd, mdd->id));
308 for (last = (struct monitor_driverdata *)CDD(GfxBase); last->next; last = last->next)
310 D(bug("[AddDisplayDriverA] Current 0x%p, next 0x%p, ID 0x%08lX\n", last, last->next, last->next->id));
311 if (mdd->id < last->next->id)
312 break;
315 D(bug("[AddDisplayDriverA] Inserting after 0x%p\n", last));
316 mdd->next = last->next;
317 last->next = mdd;
319 /* Remember next available ID */
320 if (NextID > CDD(GfxBase)->last_id)
321 CDD(GfxBase)->last_id = NextID;
323 /* Return the assigned ID if the caller asked to do so */
324 if (ResultID)
325 *ResultID = FirstID;
327 else /* if (mdd) */
329 OOP_DisposeObject(gfxhidd);
330 ret = DD_NO_MEM;
333 else /* if (gfxhidd) */
335 ret = DD_DRIVER_ERROR;
337 } /* if (ret == DD_OK) */
339 ReleaseSemaphore(&CDD(GfxBase)->displaydb_sem);
341 /* Set the first non-boot non-planar driver as default */
342 if ((ret == DD_OK) && (!GfxBase->default_monitor) && (!(mdd->flags & DF_BootMode)))
345 * Amiga(tm) chipset driver does not become a default.
346 * This is done because RTG modes (if any) are commonly preferred
347 * over it.
348 * TODO: in future some prefs program could be implemented. It would
349 * allow the user to describe the physical placement of several displays
350 * in his environment, and explicitly set the preferred display.
352 if (!IS_CLASS(gfxclass, "hidd.gfx.amigavideo"))
355 * graphics.library uses struct MonitorSpec pointers for historical reasons,
356 * so we satisfy it.
357 * Here we just get the first available sync object from the driver and
358 * set default_monitor fo its MonitorSpec. This allows BestModeIDA() to
359 * obtain preferred monitor back from this MonitorSpec (by asking the associated
360 * sync object about its parent driver).
362 * TODO:
363 * Originally display drivers in AmigaOS had a concept of "preferred mode ID".
364 * Every driver supplied own hardcoded ID which can be retrieved by GetDisplayInfoData()
365 * in MonitorInfo->PreferredModeID. Currently AROS does not implement this concept.
366 * However this sync could be a preferred mode's sync.
367 * It needs to be researched what exactly this mode ID is. Implementing this concept would
368 * improve AmigaOS(tm) compatibility.
370 OOP_Object *sync = HIDD_Gfx_GetSync(mdd->gfxhidd_orig, 0);
372 OOP_GetAttr(sync, aHidd_Sync_MonitorSpec, (IPTR *)&GfxBase->default_monitor);
376 D(bug("[AddDisplayDriverA] Returning %u\n", ret));
377 return ret;
379 AROS_LIBFUNC_EXIT
380 } /* LateGfxInit */