summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jacinto6/sgx_src/eurasia_km/services4/3rdparty/dc_sunxi/dc_sunxi_displayclass.c')
-rw-r--r--jacinto6/sgx_src/eurasia_km/services4/3rdparty/dc_sunxi/dc_sunxi_displayclass.c1765
1 files changed, 1765 insertions, 0 deletions
diff --git a/jacinto6/sgx_src/eurasia_km/services4/3rdparty/dc_sunxi/dc_sunxi_displayclass.c b/jacinto6/sgx_src/eurasia_km/services4/3rdparty/dc_sunxi/dc_sunxi_displayclass.c
new file mode 100644
index 0000000..6af20f6
--- /dev/null
+++ b/jacinto6/sgx_src/eurasia_km/services4/3rdparty/dc_sunxi/dc_sunxi_displayclass.c
@@ -0,0 +1,1765 @@
1/*************************************************************************/ /*!
2@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
3@License Dual MIT/GPLv2
4
5The contents of this file are subject to the MIT license as set out below.
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17Alternatively, the contents of this file may be used under the terms of
18the GNU General Public License Version 2 ("GPL") in which case the provisions
19of GPL are applicable instead of those above.
20
21If you wish to allow use of your version of this file only under the terms of
22GPL, and not to allow others to use your version of this file under the terms
23of the MIT license, indicate your decision by deleting the provisions above
24and replace them with the notice and other provisions required by GPL as set
25out in the file called "GPL-COPYING" included in this distribution. If you do
26not delete the provisions above, a recipient may use your version of this file
27under the terms of either the MIT license or GPL.
28
29This License is also included in this distribution in the file called
30"MIT-COPYING".
31
32EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
33PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
34BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
36COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
37IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
38CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39*/ /**************************************************************************/
40
41#include <linux/version.h>
42#include <linux/kernel.h>
43#include <linux/console.h>
44#include <linux/fb.h>
45#include <linux/module.h>
46#include <linux/string.h>
47#include <linux/notifier.h>
48
49
50#if defined(SUPPORT_DRI_DRM)
51#include <drm/drmP.h>
52#endif
53
54/* IMG services headers */
55#include "img_defs.h"
56#include "servicesext.h"
57#include "kerneldisplay.h"
58#include "linux/drv_display.h"
59
60#if defined(SUPPORT_DRI_DRM)
61#include "pvr_drm.h"
62#else
63#include "pvrmodule.h"
64#endif
65
66#define DC_SUNXI_COMMAND_COUNT 1
67
68#define DC_SUNXI_VSYNC_SETTLE_COUNT 5
69
70#define DC_SUNXI_MAX_NUM_DEVICES 1
71
72//#define DC_SUNXI_DISPC_GRALLOC_QUEUE_IN_V1_PATH
73
74#if (DC_SUNXI_MAX_NUM_DEVICES > FB_MAX)
75#error DC_SUNXI_MAX_NUM_DEVICES must not be greater than FB_MAX
76#endif
77
78/* DEBUG only printk */
79#ifdef DEBUG
80#define DEBUG_PRINTK(x) printk x
81#else
82#define DEBUG_PRINTK(x)
83#endif
84
85#define DISPLAY_DEVICE_NAME "SUNXI Linux Display Driver"
86#define DRVNAME "dc_sunxi"
87#define DEVNAME DRVNAME
88#define DRIVER_PREFIX DRVNAME
89
90#ifndef UNREFERENCED_PARAMETER
91#define UNREFERENCED_PARAMETER(param) (param) = (param)
92#endif
93
94#if !defined(SUPPORT_DRI_DRM)
95MODULE_SUPPORTED_DEVICE(DEVNAME);
96#endif
97
98/* DC_SUNXI buffer structure */
99typedef struct DC_SUNXI_BUFFER_TAG
100{
101 struct DC_SUNXI_BUFFER_TAG *psNext;
102 struct DC_SUNXI_DEVINFO_TAG *psDevInfo;
103
104 struct work_struct sWork;
105
106 /* Position of this buffer in the virtual framebuffer */
107 unsigned long ulYOffset;
108
109 /* IMG structures used, to minimise API function code */
110 /* replace with own structures where necessary */
111 IMG_SYS_PHYADDR sSysAddr;
112 IMG_CPU_VIRTADDR sCPUVAddr;
113 PVRSRV_SYNC_DATA *psSyncData;
114
115 void *hCmdComplete;
116 unsigned long ulSwapInterval;
117
118} DC_SUNXI_BUFFER;
119
120/* DC_SUNXI swapchain structure */
121typedef struct DC_SUNXI_SWAPCHAIN_TAG
122{
123 /* Swap chain ID */
124 unsigned int uiSwapChainID;
125
126 /* number of buffers in swapchain */
127 unsigned long ulBufferCount;
128
129 /* list of buffers in the swapchain */
130 DC_SUNXI_BUFFER *psBuffer;
131
132 /* Swap chain work queue */
133 struct workqueue_struct *psWorkQueue;
134
135 /*
136 * Set if we didn't manage to wait for VSync on last swap,
137 * or if we think we need to wait for VSync on the next flip.
138 * The flag helps to avoid jitter when the screen is
139 * unblanked, by forcing an extended wait for VSync before
140 * attempting the next flip.
141 */
142 bool bNotVSynced;
143
144 /* Framebuffer Device ID for messages (e.g. printk) */
145 unsigned int uiFBDevID;
146
147} DC_SUNXI_SWAPCHAIN;
148
149typedef struct DC_SUNXI_FBINFO_TAG
150{
151 unsigned long ulFBSize;
152 unsigned long ulBufferSize;
153 unsigned long ulRoundedBufferSize;
154 unsigned long ulWidth;
155 unsigned long ulHeight;
156 unsigned long ulByteStride;
157 unsigned long ulPhysicalWidthmm;
158 unsigned long ulPhysicalHeightmm;
159
160 /* IMG structures used, to minimise API function code */
161 /* replace with own structures where necessary */
162 IMG_SYS_PHYADDR sSysAddr;
163 IMG_CPU_VIRTADDR sCPUVAddr;
164
165 /* pixelformat of system/primary surface */
166 PVRSRV_PIXEL_FORMAT ePixelFormat;
167
168} DC_SUNXI_FBINFO;
169
170/* kernel device information structure */
171typedef struct DC_SUNXI_DEVINFO_TAG
172{
173 /* Framebuffer Device ID */
174 unsigned int uiFBDevID;
175
176 /* PVR Device ID */
177 unsigned int uiPVRDevID;
178
179 /* Swapchain create/destroy mutex */
180 struct mutex sCreateSwapChainMutex;
181
182 /* system surface info */
183 DC_SUNXI_BUFFER sSystemBuffer;
184
185 /* jump table into PVR services */
186 PVRSRV_DC_DISP2SRV_KMJTABLE sPVRJTable;
187
188 /* jump table into DC */
189 PVRSRV_DC_SRV2DISP_KMJTABLE sDCJTable;
190
191 /* fb info structure */
192 DC_SUNXI_FBINFO sFBInfo;
193
194 /* Only one swapchain supported by this device so hang it here */
195 DC_SUNXI_SWAPCHAIN *psSwapChain;
196
197 /* Swap chain ID */
198 unsigned int uiSwapChainID;
199
200 /* True if PVR Services is flushing its command queues */
201 atomic_t sFlushCommands;
202
203 /* pointer to linux frame buffer information structure */
204 struct fb_info *psLINFBInfo;
205
206 /* IMG structures used, to minimise API function code */
207 /* replace with own structures where necessary */
208
209 DISPLAY_INFO sDisplayInfo;
210
211 /* Display format */
212 DISPLAY_FORMAT sDisplayFormat;
213
214 /* Display dimensions */
215 DISPLAY_DIMS sDisplayDim;
216
217 /* Number of blank/unblank events */
218 atomic_t sBlankEvents;
219
220} DC_SUNXI_DEVINFO;
221
222typedef enum _DC_SUNXI_ERROR_
223{
224 DC_SUNXI_OK = 0,
225 DC_SUNXI_ERROR_GENERIC = 1,
226 DC_SUNXI_ERROR_INIT_FAILURE = 2,
227 DC_SUNXI_ERROR_INVALID_DEVICE = 3,
228
229} DC_SUNXI_ERROR;
230
231static DC_SUNXI_DEVINFO *gapsDevInfo[DC_SUNXI_MAX_NUM_DEVICES];
232
233/* Top level 'hook ptr' */
234static PFN_DC_GET_PVRJTABLE gpfnGetPVRJTable;
235
236/* Don't wait for vertical sync */
237static inline bool DontWaitForVSync(DC_SUNXI_DEVINFO *psDevInfo)
238{
239 return atomic_read(&psDevInfo->sFlushCommands);
240}
241
242/*
243 * Called after the screen has unblanked, or after any other occasion
244 * when we didn't wait for vsync, but now need to. Not doing this after
245 * unblank leads to screen jitter on some screens.
246 * Returns true if the screen has been deemed to have settled.
247 */
248static bool WaitForVSyncSettle(DC_SUNXI_DEVINFO *psDevInfo)
249{
250 unsigned int i;
251 for(i = 0; i < DC_SUNXI_VSYNC_SETTLE_COUNT; i++)
252 {
253 if (DontWaitForVSync(psDevInfo))
254 {
255 return false;
256 }
257 }
258
259 return true;
260}
261
262/* Flip display to given buffer */
263static void DC_SUNXIFlip(DC_SUNXI_DEVINFO *psDevInfo, DC_SUNXI_BUFFER *psBuffer)
264{
265 struct fb_var_screeninfo sFBVar;
266 unsigned long ulYResVirtual;
267 int res;
268
269 console_lock();
270
271 sFBVar = psDevInfo->psLINFBInfo->var;
272
273 sFBVar.xoffset = 0;
274 sFBVar.yoffset = psBuffer->ulYOffset;
275
276 ulYResVirtual = psBuffer->ulYOffset + sFBVar.yres;
277
278 res = fb_pan_display(psDevInfo->psLINFBInfo, &sFBVar);
279 if (res != 0)
280 {
281 printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: fb_pan_display failed (Y Offset: %lu, Error: %d)\n", __FUNCTION__, psDevInfo->uiFBDevID, psBuffer->ulYOffset, res);
282 }
283
284 console_unlock();
285}
286
287/*
288 * Swap handler.
289 * Called from the swap chain work queue handler.
290 * There is no need to take the swap chain creation lock in here, or use
291 * some other method of stopping the swap chain from being destroyed.
292 * This is because the swap chain creation lock is taken when queueing work,
293 * and the work queue is flushed before the swap chain is destroyed.
294 */
295static void DC_SUNXISwapHandler(DC_SUNXI_BUFFER *psBuffer)
296{
297 DC_SUNXI_DEVINFO *psDevInfo = psBuffer->psDevInfo;
298 DC_SUNXI_SWAPCHAIN *psSwapChain = psDevInfo->psSwapChain;
299 bool bPreviouslyNotVSynced;
300
301 DC_SUNXIFlip(psDevInfo, psBuffer);
302
303 bPreviouslyNotVSynced = psSwapChain->bNotVSynced;
304 psSwapChain->bNotVSynced = true;
305
306 if (!DontWaitForVSync(psDevInfo))
307 {
308 psSwapChain->bNotVSynced = false;
309
310 if (bPreviouslyNotVSynced)
311 {
312 psSwapChain->bNotVSynced = !WaitForVSyncSettle(psDevInfo);
313 }
314 else if (psBuffer->ulSwapInterval != 0)
315 {
316 psSwapChain->bNotVSynced = IMG_FALSE;
317 }
318 }
319
320 psDevInfo->sPVRJTable.pfnPVRSRVCmdComplete((IMG_HANDLE)psBuffer->hCmdComplete, IMG_TRUE);
321}
322
323/* Process an item on a swap chain work queue */
324static void WorkQueueHandler(struct work_struct *psWork)
325{
326 DC_SUNXI_BUFFER *psBuffer = container_of(psWork, DC_SUNXI_BUFFER, sWork);
327
328 DC_SUNXISwapHandler(psBuffer);
329}
330
331/* Create a swap chain work queue */
332static DC_SUNXI_ERROR DC_SUNXICreateSwapQueue(DC_SUNXI_SWAPCHAIN *psSwapChain)
333{
334 /*
335 * Calling alloc_ordered_workqueue with the WQ_FREEZABLE and
336 * WQ_MEM_RECLAIM flags set, (currently) has the same effect as
337 * calling create_freezable_workqueue. None of the other WQ
338 * flags are valid. Setting WQ_MEM_RECLAIM should allow the
339 * workqueue to continue to service the swap chain in low memory
340 * conditions, preventing the driver from holding on to
341 * resources longer than it needs to.
342 */
343 psSwapChain->psWorkQueue = alloc_ordered_workqueue(DEVNAME, WQ_FREEZABLE | WQ_MEM_RECLAIM);
344 if (psSwapChain->psWorkQueue == NULL)
345 {
346 printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: Couldn't create workqueue\n", __FUNCTION__, psSwapChain->uiFBDevID);
347
348 return (DC_SUNXI_ERROR_INIT_FAILURE);
349 }
350
351 return (DC_SUNXI_OK);
352}
353
354/* Prepare buffer for insertion into a swap chain work queue */
355static void DC_SUNXIInitBufferForSwap(DC_SUNXI_BUFFER *psBuffer)
356{
357 INIT_WORK(&psBuffer->sWork, WorkQueueHandler);
358}
359
360/* Destroy a swap chain work queue */
361static void DC_SUNXIDestroySwapQueue(DC_SUNXI_SWAPCHAIN *psSwapChain)
362{
363 destroy_workqueue(psSwapChain->psWorkQueue);
364}
365
366/* Unblank the screen */
367static DC_SUNXI_ERROR DC_SUNXIUnblankDisplay(DC_SUNXI_DEVINFO *psDevInfo)
368{
369 int res;
370
371 console_lock();
372 res = fb_blank(psDevInfo->psLINFBInfo, 0);
373 console_unlock();
374
375 if (res != 0 && res != -EINVAL)
376 {
377 printk(KERN_ERR DRIVER_PREFIX
378 ": %s: Device %u: fb_blank failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res);
379 return (DC_SUNXI_ERROR_GENERIC);
380 }
381
382 return (DC_SUNXI_OK);
383}
384
385/* Round x up to a multiple of y */
386static inline unsigned long RoundUpToMultiple(unsigned long x, unsigned long y)
387{
388 unsigned long div = x / y;
389 unsigned long rem = x % y;
390
391 return (div + ((rem == 0) ? 0 : 1)) * y;
392}
393
394/* Greatest common divisor of x and y */
395static unsigned long GCD(unsigned long x, unsigned long y)
396{
397 while (y != 0)
398 {
399 unsigned long r = x % y;
400 x = y;
401 y = r;
402 }
403
404 return x;
405}
406
407/* Least common multiple of x and y */
408static unsigned long LCM(unsigned long x, unsigned long y)
409{
410 unsigned long gcd = GCD(x, y);
411
412 return (gcd == 0) ? 0 : ((x / gcd) * y);
413}
414
415static unsigned DC_SUNXIMaxFBDevIDPlusOne(void)
416{
417 return DC_SUNXI_MAX_NUM_DEVICES;
418}
419
420/* Returns DevInfo pointer for a given device */
421static DC_SUNXI_DEVINFO *DC_SUNXIGetDevInfoPtr(unsigned uiFBDevID)
422{
423 WARN_ON(uiFBDevID >= DC_SUNXIMaxFBDevIDPlusOne());
424
425 if (uiFBDevID >= DC_SUNXI_MAX_NUM_DEVICES)
426 {
427 return NULL;
428 }
429
430 return gapsDevInfo[uiFBDevID];
431}
432
433/* Sets the DevInfo pointer for a given device */
434static inline void DC_SUNXISetDevInfoPtr(unsigned uiFBDevID, DC_SUNXI_DEVINFO *psDevInfo)
435{
436 WARN_ON(uiFBDevID >= DC_SUNXI_MAX_NUM_DEVICES);
437
438 if (uiFBDevID < DC_SUNXI_MAX_NUM_DEVICES)
439 {
440 gapsDevInfo[uiFBDevID] = psDevInfo;
441 }
442}
443
444static inline bool SwapChainHasChanged(DC_SUNXI_DEVINFO *psDevInfo, DC_SUNXI_SWAPCHAIN *psSwapChain)
445{
446 return (psDevInfo->psSwapChain != psSwapChain) ||
447 (psDevInfo->uiSwapChainID != psSwapChain->uiSwapChainID);
448}
449
450extern int dispc_gralloc_queue(setup_dispc_data_t *psDispcData, int ui32DispcDataLength, void (*cb_fn)(void *, int), void *cb_arg);
451
452static void
453QueueBufferImmediate(DC_SUNXI_DEVINFO *psDevInfo, IMG_SYS_PHYADDR sSysAddr,
454 void (*cb_fn)(void *, int), void *cb_arg)
455{
456 setup_dispc_data_t sDispcData =
457 {
458 .post2_layers = 1,
459 .primary_display_layer_num = 1,
460 .layer_info =
461 {
462 [0] =
463 {
464 .mode = DISP_LAYER_WORK_MODE_NORMAL,
465 .src_win =
466 {
467 .width = psDevInfo->sFBInfo.ulWidth,
468 .height = psDevInfo->sFBInfo.ulHeight
469 },
470 .scn_win =
471 {
472 .width = psDevInfo->sFBInfo.ulWidth,
473 .height = psDevInfo->sFBInfo.ulHeight
474 },
475 .alpha_en = 1,
476 .alpha_val = 0xff,
477 .fb =
478 {
479 .mode = DISP_MOD_INTERLEAVED,
480 .format = DISP_FORMAT_ARGB8888,
481 .seq = DISP_SEQ_ARGB,
482 .pre_multiply = 1,
483 .size =
484 {
485 .width = psDevInfo->sFBInfo.ulWidth,
486 .height = psDevInfo->sFBInfo.ulHeight
487 },
488 .addr[0] = sSysAddr.uiAddr,
489 },
490 },
491 },
492 };
493
494 dispc_gralloc_queue(&sDispcData, sizeof(setup_dispc_data_t), cb_fn, cb_arg);
495}
496
497static void dispc_proxy_cmdcomplete(void * cookie, int i)
498{
499 /* Workaround for bug in sunxi kernel, where it uses the latest cb_fn
500 * with older cb_arg (they should be used as a pair).
501 */
502 if (cookie == (void *)0xdeadbeef)
503 return;
504
505 /* XXX: assumes that there is only one display */
506 gapsDevInfo[0]->sPVRJTable.pfnPVRSRVCmdComplete(cookie, i);
507}
508
509/*
510 * SetDCState
511 * Called from services.
512 */
513static IMG_VOID SetDCState(IMG_HANDLE hDevice, IMG_UINT32 ui32State)
514{
515 DC_SUNXI_DEVINFO *psDevInfo = (DC_SUNXI_DEVINFO *)hDevice;
516
517 switch (ui32State)
518 {
519 case DC_STATE_FLUSH_COMMANDS:
520 /* Flush out any 'real' operation waiting for another flip.
521 * In flush state we won't pass any 'real' operations along
522 * to dispc_gralloc_queue(); we'll just CmdComplete them
523 * immediately.
524 */
525 QueueBufferImmediate(psDevInfo, psDevInfo->sSystemBuffer.sSysAddr,
526 dispc_proxy_cmdcomplete, (void *)0xdeadbeef);
527 atomic_set(&psDevInfo->sFlushCommands, true);
528 break;
529 case DC_STATE_NO_FLUSH_COMMANDS:
530 atomic_set(&psDevInfo->sFlushCommands, false);
531 break;
532 default:
533 break;
534 }
535}
536
537/*
538 * OpenDCDevice
539 * Called from services.
540 */
541static PVRSRV_ERROR OpenDCDevice(IMG_UINT32 uiPVRDevID,
542 IMG_HANDLE *phDevice,
543 PVRSRV_SYNC_DATA* psSystemBufferSyncData)
544{
545 DC_SUNXI_DEVINFO *psDevInfo;
546 DC_SUNXI_ERROR eError;
547 unsigned int i, uiMaxFBDevIDPlusOne;
548
549 if (!try_module_get(THIS_MODULE))
550 {
551 return PVRSRV_ERROR_UNABLE_TO_OPEN_DC_DEVICE;
552 }
553
554 uiMaxFBDevIDPlusOne = DC_SUNXIMaxFBDevIDPlusOne();
555
556 for (i = 0; i < uiMaxFBDevIDPlusOne; i++)
557 {
558 psDevInfo = DC_SUNXIGetDevInfoPtr(i);
559 if (psDevInfo != NULL && psDevInfo->uiPVRDevID == uiPVRDevID)
560 {
561 break;
562 }
563 }
564
565 if (i == uiMaxFBDevIDPlusOne)
566 {
567 DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX
568 ": %s: PVR Device %u not found\n", __FUNCTION__, uiPVRDevID));
569 eError = PVRSRV_ERROR_INVALID_DEVICE;
570 goto ErrorModulePut;
571 }
572
573 /* store the system surface sync data */
574 psDevInfo->sSystemBuffer.psSyncData = psSystemBufferSyncData;
575
576 eError = DC_SUNXIUnblankDisplay(psDevInfo);
577 if (eError != DC_SUNXI_OK)
578 {
579 DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX
580 ": %s: Device %u: DC_SUNXIUnblankDisplay failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, eError));
581 eError = PVRSRV_ERROR_UNBLANK_DISPLAY_FAILED;
582 goto ErrorModulePut;
583 }
584
585 /* return handle to the devinfo */
586 *phDevice = (IMG_HANDLE)psDevInfo;
587
588 return PVRSRV_OK;
589
590ErrorModulePut:
591 module_put(THIS_MODULE);
592
593 return eError;
594}
595
596/*
597 * CloseDCDevice
598 * Called from services.
599 */
600static PVRSRV_ERROR CloseDCDevice(IMG_HANDLE hDevice)
601{
602 UNREFERENCED_PARAMETER(hDevice);
603
604 module_put(THIS_MODULE);
605
606 return PVRSRV_OK;
607}
608
609/*
610 * EnumDCFormats
611 * Called from services.
612 */
613static PVRSRV_ERROR EnumDCFormats(IMG_HANDLE hDevice,
614 IMG_UINT32 *pui32NumFormats,
615 DISPLAY_FORMAT *psFormat)
616{
617 DC_SUNXI_DEVINFO *psDevInfo;
618
619 if(!hDevice || !pui32NumFormats)
620 {
621 return PVRSRV_ERROR_INVALID_PARAMS;
622 }
623
624 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
625
626 *pui32NumFormats = 1;
627
628 if(psFormat)
629 {
630 psFormat[0] = psDevInfo->sDisplayFormat;
631 }
632
633 return PVRSRV_OK;
634}
635
636/*
637 * EnumDCDims
638 * Called from services.
639 */
640static PVRSRV_ERROR EnumDCDims(IMG_HANDLE hDevice,
641 DISPLAY_FORMAT *psFormat,
642 IMG_UINT32 *pui32NumDims,
643 DISPLAY_DIMS *psDim)
644{
645 DC_SUNXI_DEVINFO *psDevInfo;
646
647 if(!hDevice || !psFormat || !pui32NumDims)
648 {
649 return PVRSRV_ERROR_INVALID_PARAMS;
650 }
651
652 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
653
654 *pui32NumDims = 1;
655
656 /* No need to look at psFormat; there is only one */
657 if(psDim)
658 {
659 psDim[0] = psDevInfo->sDisplayDim;
660 }
661
662 return PVRSRV_OK;
663}
664
665
666/*
667 * GetDCSystemBuffer
668 * Called from services.
669 */
670static PVRSRV_ERROR GetDCSystemBuffer(IMG_HANDLE hDevice, IMG_HANDLE *phBuffer)
671{
672 DC_SUNXI_DEVINFO *psDevInfo;
673
674 if(!hDevice || !phBuffer)
675 {
676 return PVRSRV_ERROR_INVALID_PARAMS;
677 }
678
679 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
680
681 *phBuffer = (IMG_HANDLE)&psDevInfo->sSystemBuffer;
682
683 return PVRSRV_OK;
684}
685
686
687/*
688 * GetDCInfo
689 * Called from services.
690 */
691static PVRSRV_ERROR GetDCInfo(IMG_HANDLE hDevice, DISPLAY_INFO *psDCInfo)
692{
693 DC_SUNXI_DEVINFO *psDevInfo;
694
695 if(!hDevice || !psDCInfo)
696 {
697 return PVRSRV_ERROR_INVALID_PARAMS;
698 }
699
700 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
701
702 *psDCInfo = psDevInfo->sDisplayInfo;
703
704 return PVRSRV_OK;
705}
706
707/*
708 * GetDCBufferAddr
709 * Called from services.
710 */
711static PVRSRV_ERROR GetDCBufferAddr(IMG_HANDLE hDevice,
712 IMG_HANDLE hBuffer,
713 IMG_SYS_PHYADDR **ppsSysAddr,
714 IMG_UINT32 *pui32ByteSize,
715 IMG_VOID **ppvCpuVAddr,
716 IMG_HANDLE *phOSMapInfo,
717 IMG_BOOL *pbIsContiguous,
718 IMG_UINT32 *pui32TilingStride)
719{
720 DC_SUNXI_DEVINFO *psDevInfo;
721 DC_SUNXI_BUFFER *psSystemBuffer;
722
723 UNREFERENCED_PARAMETER(pui32TilingStride);
724
725 if(!hDevice)
726 {
727 return PVRSRV_ERROR_INVALID_PARAMS;
728 }
729
730 if(!hBuffer)
731 {
732 return PVRSRV_ERROR_INVALID_PARAMS;
733 }
734
735 if (!ppsSysAddr)
736 {
737 return PVRSRV_ERROR_INVALID_PARAMS;
738 }
739
740 if (!pui32ByteSize)
741 {
742 return PVRSRV_ERROR_INVALID_PARAMS;
743 }
744
745 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
746
747 psSystemBuffer = (DC_SUNXI_BUFFER *)hBuffer;
748
749 *ppsSysAddr = &psSystemBuffer->sSysAddr;
750
751 *pui32ByteSize = (IMG_UINT32)psDevInfo->sFBInfo.ulBufferSize;
752
753 if (ppvCpuVAddr)
754 {
755 *ppvCpuVAddr = psSystemBuffer->sCPUVAddr;
756 }
757
758 if (phOSMapInfo)
759 {
760 *phOSMapInfo = (IMG_HANDLE)0;
761 }
762
763 if (pbIsContiguous)
764 {
765 *pbIsContiguous = IMG_TRUE;
766 }
767
768 return PVRSRV_OK;
769}
770
771/*
772 * CreateDCSwapChain
773 * Called from services.
774 */
775static PVRSRV_ERROR CreateDCSwapChain(IMG_HANDLE hDevice,
776 IMG_UINT32 ui32Flags,
777 DISPLAY_SURF_ATTRIBUTES *psDstSurfAttrib,
778 DISPLAY_SURF_ATTRIBUTES *psSrcSurfAttrib,
779 IMG_UINT32 ui32BufferCount,
780 PVRSRV_SYNC_DATA **ppsSyncData,
781 IMG_UINT32 ui32OEMFlags,
782 IMG_HANDLE *phSwapChain,
783 IMG_UINT32 *pui32SwapChainID)
784{
785 DC_SUNXI_SWAPCHAIN *psSwapChain;
786 DC_SUNXI_DEVINFO *psDevInfo;
787 PVRSRV_ERROR eError;
788 IMG_UINT32 i;
789
790 UNREFERENCED_PARAMETER(ui32OEMFlags);
791 UNREFERENCED_PARAMETER(ui32Flags);
792
793 /* Check parameters */
794 if(!hDevice
795 || !psDstSurfAttrib
796 || !psSrcSurfAttrib
797 || !ppsSyncData
798 || !phSwapChain)
799 {
800 return PVRSRV_ERROR_INVALID_PARAMS;
801 }
802
803 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
804
805 /* Do we support swap chains? */
806 if (psDevInfo->sDisplayInfo.ui32MaxSwapChains == 0)
807 {
808 return PVRSRV_ERROR_NOT_SUPPORTED;
809 }
810
811 mutex_lock(&psDevInfo->sCreateSwapChainMutex);
812
813 /* The driver only supports a single swapchain */
814 if(psDevInfo->psSwapChain != NULL)
815 {
816 eError = PVRSRV_ERROR_FLIP_CHAIN_EXISTS;
817 goto ExitUnLock;
818 }
819
820 /* create a swapchain structure */
821 psSwapChain = (DC_SUNXI_SWAPCHAIN*)kmalloc(sizeof(DC_SUNXI_SWAPCHAIN), GFP_KERNEL);
822 if(!psSwapChain)
823 {
824 eError = PVRSRV_ERROR_OUT_OF_MEMORY;
825 goto ExitUnLock;
826 }
827
828 /* If services asks for a 0-length swap chain, it's probably Android.
829 *
830 * This will use only non-display memory posting via PVRSRVSwapToDCBuffers2(),
831 * and we can skip some useless sanity checking.
832 */
833 if(ui32BufferCount > 0)
834 {
835 IMG_UINT32 ui32BuffersToSkip;
836
837 /* Check the buffer count */
838 if(ui32BufferCount > psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers)
839 {
840 eError = PVRSRV_ERROR_TOOMANYBUFFERS;
841 goto ErrorFreeSwapChain;
842 }
843
844 if ((psDevInfo->sFBInfo.ulRoundedBufferSize * (unsigned long)ui32BufferCount) > psDevInfo->sFBInfo.ulFBSize)
845 {
846 eError = PVRSRV_ERROR_TOOMANYBUFFERS;
847 goto ErrorFreeSwapChain;
848 }
849
850 /*
851 * We will allocate the swap chain buffers at the back of the frame
852 * buffer area. This preserves the front portion, which may be being
853 * used by other Linux Framebuffer based applications.
854 */
855 ui32BuffersToSkip = psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers - ui32BufferCount;
856
857 /*
858 * Verify the DST/SRC attributes,
859 * SRC/DST must match the current display mode config
860 */
861 if(psDstSurfAttrib->pixelformat != psDevInfo->sDisplayFormat.pixelformat
862 || psDstSurfAttrib->sDims.ui32ByteStride != psDevInfo->sDisplayDim.ui32ByteStride
863 || psDstSurfAttrib->sDims.ui32Width != psDevInfo->sDisplayDim.ui32Width
864 || psDstSurfAttrib->sDims.ui32Height != psDevInfo->sDisplayDim.ui32Height)
865 {
866 /* DST doesn't match the current mode */
867 eError = PVRSRV_ERROR_INVALID_PARAMS;
868 goto ErrorFreeSwapChain;
869 }
870
871 if(psDstSurfAttrib->pixelformat != psSrcSurfAttrib->pixelformat
872 || psDstSurfAttrib->sDims.ui32ByteStride != psSrcSurfAttrib->sDims.ui32ByteStride
873 || psDstSurfAttrib->sDims.ui32Width != psSrcSurfAttrib->sDims.ui32Width
874 || psDstSurfAttrib->sDims.ui32Height != psSrcSurfAttrib->sDims.ui32Height)
875 {
876 /* DST doesn't match the SRC */
877 eError = PVRSRV_ERROR_INVALID_PARAMS;
878 goto ErrorFreeSwapChain;
879 }
880
881 psSwapChain->psBuffer = (DC_SUNXI_BUFFER*)kmalloc(sizeof(DC_SUNXI_BUFFER) * ui32BufferCount, GFP_KERNEL);
882 if(!psSwapChain->psBuffer)
883 {
884 eError = PVRSRV_ERROR_OUT_OF_MEMORY;
885 goto ErrorFreeSwapChain;
886 }
887
888 /* Link the buffers */
889 for(i = 0; i < ui32BufferCount - 1; i++)
890 {
891 psSwapChain->psBuffer[i].psNext = &psSwapChain->psBuffer[i + 1];
892 }
893
894 /* and link last to first */
895 psSwapChain->psBuffer[i].psNext = &psSwapChain->psBuffer[0];
896
897 /* Configure the swapchain buffers */
898 for(i = 0; i < ui32BufferCount; i++)
899 {
900 IMG_UINT32 ui32SwapBuffer = i + ui32BuffersToSkip;
901 IMG_UINT32 ui32BufferOffset = ui32SwapBuffer * (IMG_UINT32)psDevInfo->sFBInfo.ulRoundedBufferSize;
902
903 psSwapChain->psBuffer[i].psSyncData = ppsSyncData[i];
904
905 psSwapChain->psBuffer[i].sSysAddr.uiAddr = psDevInfo->sFBInfo.sSysAddr.uiAddr + ui32BufferOffset;
906 psSwapChain->psBuffer[i].sCPUVAddr = psDevInfo->sFBInfo.sCPUVAddr + ui32BufferOffset;
907 psSwapChain->psBuffer[i].ulYOffset = ui32BufferOffset / psDevInfo->sFBInfo.ulByteStride;
908 psSwapChain->psBuffer[i].psDevInfo = psDevInfo;
909
910 DC_SUNXIInitBufferForSwap(&psSwapChain->psBuffer[i]);
911 }
912 }
913 else
914 {
915 psSwapChain->psBuffer = NULL;
916 }
917
918 psSwapChain->ulBufferCount = (unsigned long)ui32BufferCount;
919 psSwapChain->bNotVSynced = true;
920 psSwapChain->uiFBDevID = psDevInfo->uiFBDevID;
921
922 if (DC_SUNXICreateSwapQueue(psSwapChain) != DC_SUNXI_OK)
923 {
924 printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Failed to create workqueue\n", __FUNCTION__, psDevInfo->uiFBDevID);
925 eError = PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR;
926 goto ErrorFreeBuffers;
927 }
928
929 psDevInfo->uiSwapChainID++;
930 if (psDevInfo->uiSwapChainID == 0)
931 {
932 psDevInfo->uiSwapChainID++;
933 }
934
935 psSwapChain->uiSwapChainID = psDevInfo->uiSwapChainID;
936
937 psDevInfo->psSwapChain = psSwapChain;
938
939 *pui32SwapChainID = psDevInfo->uiSwapChainID;
940
941 *phSwapChain = (IMG_HANDLE)psSwapChain;
942
943 eError = PVRSRV_OK;
944 goto ExitUnLock;
945
946ErrorFreeBuffers:
947 if(psSwapChain->psBuffer)
948 {
949 kfree(psSwapChain->psBuffer);
950 }
951ErrorFreeSwapChain:
952 kfree(psSwapChain);
953ExitUnLock:
954 mutex_unlock(&psDevInfo->sCreateSwapChainMutex);
955 return eError;
956}
957
958/*
959 * DestroyDCSwapChain
960 * Called from services.
961 */
962static PVRSRV_ERROR DestroyDCSwapChain(IMG_HANDLE hDevice,
963 IMG_HANDLE hSwapChain)
964{
965 DC_SUNXI_DEVINFO *psDevInfo;
966 DC_SUNXI_SWAPCHAIN *psSwapChain;
967 DC_SUNXI_ERROR eError;
968
969 /* Check parameters */
970 if(!hDevice || !hSwapChain)
971 {
972 return PVRSRV_ERROR_INVALID_PARAMS;
973 }
974
975 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
976 psSwapChain = (DC_SUNXI_SWAPCHAIN*)hSwapChain;
977
978 mutex_lock(&psDevInfo->sCreateSwapChainMutex);
979
980 if (SwapChainHasChanged(psDevInfo, psSwapChain))
981 {
982 printk(KERN_WARNING DRIVER_PREFIX
983 ": %s: Device %u: Swap chain mismatch\n", __FUNCTION__, psDevInfo->uiFBDevID);
984
985 eError = PVRSRV_ERROR_INVALID_PARAMS;
986 goto ExitUnLock;
987 }
988
989 /* The swap queue is flushed before being destroyed */
990 DC_SUNXIDestroySwapQueue(psSwapChain);
991
992 /* Free resources */
993 if (psSwapChain->psBuffer)
994 {
995 kfree(psSwapChain->psBuffer);
996 }
997 kfree(psSwapChain);
998
999 psDevInfo->psSwapChain = NULL;
1000
1001 DC_SUNXIFlip(psDevInfo, &psDevInfo->sSystemBuffer);
1002
1003 eError = PVRSRV_OK;
1004
1005ExitUnLock:
1006 mutex_unlock(&psDevInfo->sCreateSwapChainMutex);
1007 return eError;
1008}
1009
1010/*
1011 * SetDCDstRect
1012 * Called from services.
1013 */
1014static PVRSRV_ERROR SetDCDstRect(IMG_HANDLE hDevice,
1015 IMG_HANDLE hSwapChain,
1016 IMG_RECT *psRect)
1017{
1018 UNREFERENCED_PARAMETER(hDevice);
1019 UNREFERENCED_PARAMETER(hSwapChain);
1020 UNREFERENCED_PARAMETER(psRect);
1021
1022 /* Only full display swapchains on this device */
1023
1024 return PVRSRV_ERROR_NOT_SUPPORTED;
1025}
1026
1027/*
1028 * SetDCSrcRect
1029 * Called from services.
1030 */
1031static PVRSRV_ERROR SetDCSrcRect(IMG_HANDLE hDevice,
1032 IMG_HANDLE hSwapChain,
1033 IMG_RECT *psRect)
1034{
1035 UNREFERENCED_PARAMETER(hDevice);
1036 UNREFERENCED_PARAMETER(hSwapChain);
1037 UNREFERENCED_PARAMETER(psRect);
1038
1039 /* Only full display swapchains on this device */
1040
1041 return PVRSRV_ERROR_NOT_SUPPORTED;
1042}
1043
1044/*
1045 * SetDCDstColourKey
1046 * Called from services.
1047 */
1048static PVRSRV_ERROR SetDCDstColourKey(IMG_HANDLE hDevice,
1049 IMG_HANDLE hSwapChain,
1050 IMG_UINT32 ui32CKColour)
1051{
1052 UNREFERENCED_PARAMETER(hDevice);
1053 UNREFERENCED_PARAMETER(hSwapChain);
1054 UNREFERENCED_PARAMETER(ui32CKColour);
1055
1056 /* Don't support DST CK on this device */
1057
1058 return PVRSRV_ERROR_NOT_SUPPORTED;
1059}
1060
1061/*
1062 * SetDCSrcColourKey
1063 * Called from services.
1064 */
1065static PVRSRV_ERROR SetDCSrcColourKey(IMG_HANDLE hDevice,
1066 IMG_HANDLE hSwapChain,
1067 IMG_UINT32 ui32CKColour)
1068{
1069 UNREFERENCED_PARAMETER(hDevice);
1070 UNREFERENCED_PARAMETER(hSwapChain);
1071 UNREFERENCED_PARAMETER(ui32CKColour);
1072
1073 /* Don't support SRC CK on this device */
1074
1075 return PVRSRV_ERROR_NOT_SUPPORTED;
1076}
1077
1078/*
1079 * GetDCBuffers
1080 * Called from services.
1081 */
1082static PVRSRV_ERROR GetDCBuffers(IMG_HANDLE hDevice,
1083 IMG_HANDLE hSwapChain,
1084 IMG_UINT32 *pui32BufferCount,
1085 IMG_HANDLE *phBuffer)
1086{
1087 DC_SUNXI_DEVINFO *psDevInfo;
1088 DC_SUNXI_SWAPCHAIN *psSwapChain;
1089 PVRSRV_ERROR eError;
1090 unsigned int i;
1091
1092 /* Check parameters */
1093 if(!hDevice
1094 || !hSwapChain
1095 || !pui32BufferCount
1096 || !phBuffer)
1097 {
1098 return PVRSRV_ERROR_INVALID_PARAMS;
1099 }
1100
1101 psDevInfo = (DC_SUNXI_DEVINFO*)hDevice;
1102 psSwapChain = (DC_SUNXI_SWAPCHAIN*)hSwapChain;
1103
1104 mutex_lock(&psDevInfo->sCreateSwapChainMutex);
1105
1106 if (SwapChainHasChanged(psDevInfo, psSwapChain))
1107 {
1108 printk(KERN_WARNING DRIVER_PREFIX
1109 ": %s: Device %u: Swap chain mismatch\n", __FUNCTION__, psDevInfo->uiFBDevID);
1110
1111 eError = PVRSRV_ERROR_INVALID_PARAMS;
1112 goto Exit;
1113 }
1114
1115 /* Return the buffer count */
1116 *pui32BufferCount = (IMG_UINT32)psSwapChain->ulBufferCount;
1117
1118 /* Return the buffers */
1119 for(i=0; i<psSwapChain->ulBufferCount; i++)
1120 {
1121 phBuffer[i] = (IMG_HANDLE)&psSwapChain->psBuffer[i];
1122 }
1123
1124 eError = PVRSRV_OK;
1125
1126Exit:
1127 mutex_unlock(&psDevInfo->sCreateSwapChainMutex);
1128 return eError;
1129}
1130
1131/*
1132 * SwapToDCBuffer
1133 * Called from services.
1134 */
1135static PVRSRV_ERROR SwapToDCBuffer(IMG_HANDLE hDevice,
1136 IMG_HANDLE hBuffer,
1137 IMG_UINT32 ui32SwapInterval,
1138 IMG_HANDLE hPrivateTag,
1139 IMG_UINT32 ui32ClipRectCount,
1140 IMG_RECT *psClipRect)
1141{
1142 UNREFERENCED_PARAMETER(hDevice);
1143 UNREFERENCED_PARAMETER(hBuffer);
1144 UNREFERENCED_PARAMETER(ui32SwapInterval);
1145 UNREFERENCED_PARAMETER(hPrivateTag);
1146 UNREFERENCED_PARAMETER(ui32ClipRectCount);
1147 UNREFERENCED_PARAMETER(psClipRect);
1148
1149 /* * Nothing to do since Services common code does the work */
1150
1151 return PVRSRV_OK;
1152}
1153
1154/* Triggered by PVRSRVSwapToDCBuffer */
1155static IMG_BOOL ProcessFlipV1(IMG_HANDLE hCmdCookie,
1156 DC_SUNXI_DEVINFO *psDevInfo,
1157 DC_SUNXI_SWAPCHAIN *psSwapChain,
1158 DC_SUNXI_BUFFER *psBuffer,
1159 unsigned long ulSwapInterval)
1160{
1161 mutex_lock(&psDevInfo->sCreateSwapChainMutex);
1162
1163 /* The swap chain has been destroyed */
1164 if (SwapChainHasChanged(psDevInfo, psSwapChain))
1165 {
1166 DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX
1167 ": %s: Device %u (PVR Device ID %u): The swap chain has been destroyed\n",
1168 __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID));
1169 }
1170 else
1171 {
1172#if defined(DC_SUNXI_DISPC_GRALLOC_QUEUE_IN_V1_PATH)
1173 /* Not enabled by default yet until we have a way to CmdComplete
1174 * after vblank rather than after next programming!
1175 */
1176 QueueBufferImmediate(psDevInfo, psBuffer->sSysAddr,
1177 dispc_proxy_cmdcomplete, (void *)0xdeadbeef);
1178
1179 /* When dispc_gralloc_queue() can call back after programming,
1180 * this can be removed.
1181 */
1182 psDevInfo->sPVRJTable.pfnPVRSRVCmdComplete(hCmdCookie, IMG_TRUE);
1183#else /* DC_SUNXI_DISPC_GRALLOC_QUEUE_IN_V1_PATH */
1184 int res;
1185
1186 psBuffer->hCmdComplete = hCmdCookie;
1187 psBuffer->ulSwapInterval = ulSwapInterval;
1188
1189 res = queue_work(psSwapChain->psWorkQueue, &psBuffer->sWork);
1190 if (res == 0)
1191 {
1192 printk(KERN_WARNING DRIVER_PREFIX
1193 ": %s: Device %u: Buffer already on work queue\n",
1194 __FUNCTION__, psSwapChain->uiFBDevID);
1195 }
1196#endif /* DC_SUNXI_DISPC_GRALLOC_QUEUE_IN_V1_PATH */
1197 }
1198
1199 mutex_unlock(&psDevInfo->sCreateSwapChainMutex);
1200 return IMG_TRUE;
1201}
1202
1203static IMG_BOOL ProcessFlipV2(IMG_HANDLE hCmdCookie,
1204 DC_SUNXI_DEVINFO *psDevInfo,
1205 PDC_MEM_INFO *ppsMemInfos,
1206 IMG_UINT32 ui32NumMemInfos,
1207 setup_dispc_data_t *psDispcData,
1208 IMG_UINT32 ui32DispcDataLength)
1209{
1210 int i;
1211
1212 if(!psDispcData)
1213 {
1214 if(ui32NumMemInfos == 1)
1215 {
1216 IMG_CPU_PHYADDR phyAddr;
1217 IMG_SYS_PHYADDR sSysAddr;
1218
1219 psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuPAddr(ppsMemInfos[0], 0, &phyAddr);
1220 sSysAddr.uiAddr = phyAddr.uiAddr;
1221
1222 /* If we got a meminfo but no private data, assume the 'null' HWC
1223 * backend is in use, and emulate a swapchain-less ProcessFlipV1.
1224 */
1225 QueueBufferImmediate(psDevInfo, sSysAddr,
1226 dispc_proxy_cmdcomplete, hCmdCookie);
1227 }
1228 else
1229 {
1230 printk(KERN_WARNING DRIVER_PREFIX
1231 ": %s: Device %u: WARNING: psDispcData was NULL. "
1232 "The HWC probably has a bug. Silently ignoring.",
1233 __FUNCTION__, psDevInfo->uiFBDevID);
1234 gapsDevInfo[0]->sPVRJTable.pfnPVRSRVCmdComplete(hCmdCookie, IMG_TRUE);
1235 }
1236
1237 return IMG_TRUE;
1238 }
1239
1240 if(ui32DispcDataLength != sizeof(setup_dispc_data_t))
1241 {
1242 printk(KERN_WARNING DRIVER_PREFIX
1243 ": %s: Device %u: WARNING: Unexpected private data size, %u vs %u.",
1244 __FUNCTION__, psDevInfo->uiFBDevID, ui32DispcDataLength,
1245 sizeof(setup_dispc_data_t));
1246 gapsDevInfo[0]->sPVRJTable.pfnPVRSRVCmdComplete(hCmdCookie, IMG_TRUE);
1247 return IMG_TRUE;
1248 }
1249
1250 if(DontWaitForVSync(psDevInfo))
1251 {
1252 gapsDevInfo[0]->sPVRJTable.pfnPVRSRVCmdComplete(hCmdCookie, IMG_TRUE);
1253 return IMG_TRUE;
1254 }
1255
1256 /* Maximum of 8 layer_infos. Meminfo array is dynamically sized */
1257 for(i = 0; i < ui32NumMemInfos && i < 8; i++)
1258 {
1259 IMG_CPU_PHYADDR phyAddr;
1260
1261 psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuPAddr(ppsMemInfos[i], 0, &phyAddr);
1262
1263 psDispcData->layer_info[i].fb.addr[0] = phyAddr.uiAddr;
1264 }
1265
1266 dispc_gralloc_queue(psDispcData, ui32DispcDataLength, dispc_proxy_cmdcomplete, (void *)hCmdCookie);
1267
1268 return IMG_TRUE;
1269}
1270
1271/* Command processing flip handler function. Called from services. */
1272static IMG_BOOL ProcessFlip(IMG_HANDLE hCmdCookie,
1273 IMG_UINT32 ui32DataSize,
1274 IMG_VOID *pvData)
1275{
1276 DISPLAYCLASS_FLIP_COMMAND *psFlipCmd;
1277 DC_SUNXI_DEVINFO *psDevInfo;
1278
1279 /* Check parameters */
1280 if(!hCmdCookie || !pvData)
1281 {
1282 return IMG_FALSE;
1283 }
1284
1285 /* Validate data packet */
1286 psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND*)pvData;
1287
1288 if (psFlipCmd == IMG_NULL)
1289 {
1290 return IMG_FALSE;
1291 }
1292
1293 psDevInfo = (DC_SUNXI_DEVINFO*)psFlipCmd->hExtDevice;
1294
1295 if(psFlipCmd->hExtBuffer)
1296 {
1297 return ProcessFlipV1(hCmdCookie,
1298 psDevInfo,
1299 psFlipCmd->hExtSwapChain,
1300 psFlipCmd->hExtBuffer,
1301 psFlipCmd->ui32SwapInterval);
1302 }
1303 else
1304 {
1305 DISPLAYCLASS_FLIP_COMMAND2 *psFlipCmd2;
1306 psFlipCmd2 = (DISPLAYCLASS_FLIP_COMMAND2 *)pvData;
1307 return ProcessFlipV2(hCmdCookie,
1308 psDevInfo,
1309 psFlipCmd2->ppsMemInfos,
1310 psFlipCmd2->ui32NumMemInfos,
1311 psFlipCmd2->pvPrivData,
1312 psFlipCmd2->ui32PrivDataLength);
1313 }
1314}
1315
1316static DC_SUNXI_ERROR DC_SUNXIInitFBDev(DC_SUNXI_DEVINFO *psDevInfo)
1317{
1318 struct fb_info *psLINFBInfo;
1319 struct module *psLINFBOwner;
1320 DC_SUNXI_FBINFO *psPVRFBInfo = &psDevInfo->sFBInfo;
1321 DC_SUNXI_ERROR eError = DC_SUNXI_ERROR_GENERIC;
1322 unsigned int uiFBDevID = psDevInfo->uiFBDevID;
1323
1324 console_lock();
1325
1326 psLINFBInfo = registered_fb[uiFBDevID];
1327 if (psLINFBInfo == NULL)
1328 {
1329 eError = DC_SUNXI_ERROR_INVALID_DEVICE;
1330 goto ErrorRelSem;
1331 }
1332
1333 psLINFBOwner = psLINFBInfo->fbops->owner;
1334 if (!try_module_get(psLINFBOwner))
1335 {
1336 printk(KERN_INFO DRIVER_PREFIX
1337 ": %s: Device %u: Couldn't get framebuffer module\n", __FUNCTION__, uiFBDevID);
1338
1339 goto ErrorRelSem;
1340 }
1341
1342 if (psLINFBInfo->fbops->fb_open != NULL)
1343 {
1344 int res;
1345
1346 res = psLINFBInfo->fbops->fb_open(psLINFBInfo, 0);
1347 if (res != 0)
1348 {
1349 printk(KERN_INFO DRIVER_PREFIX
1350 " %s: Device %u: Couldn't open framebuffer(%d)\n", __FUNCTION__, uiFBDevID, res);
1351
1352 goto ErrorModPut;
1353 }
1354 }
1355
1356 psDevInfo->psLINFBInfo = psLINFBInfo;
1357
1358 psPVRFBInfo->ulWidth = psLINFBInfo->var.xres;
1359 psPVRFBInfo->ulHeight = psLINFBInfo->var.yres;
1360
1361 if (psPVRFBInfo->ulWidth == 0 || psPVRFBInfo->ulHeight == 0)
1362 {
1363 eError = DC_SUNXI_ERROR_INVALID_DEVICE;
1364 goto ErrorFBRel;
1365 }
1366
1367 psPVRFBInfo->ulFBSize = (psLINFBInfo->screen_size) != 0 ?
1368 psLINFBInfo->screen_size : psLINFBInfo->fix.smem_len;
1369
1370 /*
1371 * Try and filter out invalid FB info structures (a problem
1372 * seen on some systems).
1373 */
1374 if (psPVRFBInfo->ulFBSize == 0 || psLINFBInfo->fix.line_length == 0)
1375 {
1376 eError = DC_SUNXI_ERROR_INVALID_DEVICE;
1377 goto ErrorFBRel;
1378 }
1379
1380 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1381 ": Device %u: Framebuffer size: %lu\n",
1382 psDevInfo->uiFBDevID, psPVRFBInfo->ulFBSize));
1383 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1384 ": Device %u: Framebuffer virtual width: %u\n",
1385 psDevInfo->uiFBDevID, psLINFBInfo->var.xres_virtual));
1386 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1387 ": Device %u: Framebuffer virtual height: %u\n",
1388 psDevInfo->uiFBDevID, psLINFBInfo->var.yres_virtual));
1389 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1390 ": Device %u: Framebuffer width: %lu\n",
1391 psDevInfo->uiFBDevID, psPVRFBInfo->ulWidth));
1392 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1393 ": Device %u: Framebuffer height: %lu\n",
1394 psDevInfo->uiFBDevID, psPVRFBInfo->ulHeight));
1395
1396 /* System Surface */
1397 psPVRFBInfo->sSysAddr.uiAddr = psLINFBInfo->fix.smem_start;
1398 psPVRFBInfo->sCPUVAddr = psLINFBInfo->screen_base;
1399
1400 psPVRFBInfo->ulByteStride = psLINFBInfo->fix.line_length;
1401
1402 printk(KERN_WARNING
1403 "#####: Device %u: Framebuffer physical address: 0x%x\n",
1404 psDevInfo->uiFBDevID, psPVRFBInfo->sSysAddr.uiAddr);
1405
1406 if (psPVRFBInfo->sCPUVAddr != NULL)
1407 {
1408 DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX
1409 ": Device %u: Framebuffer virtual address: %p\n",
1410 psDevInfo->uiFBDevID, psPVRFBInfo->sCPUVAddr));
1411 }
1412
1413 DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX
1414 ": Device %u: Framebuffer stride: %lu\n",
1415 psDevInfo->uiFBDevID, psPVRFBInfo->ulByteStride));
1416
1417 psPVRFBInfo->ulBufferSize = psPVRFBInfo->ulHeight * psPVRFBInfo->ulByteStride;
1418
1419 {
1420 unsigned long ulLCM;
1421 ulLCM = LCM(psPVRFBInfo->ulByteStride, PAGE_SIZE);
1422
1423 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1424 ": Device %u: LCM of stride and page size: %lu\n",
1425 psDevInfo->uiFBDevID, ulLCM));
1426
1427 /* Round the buffer size up to a multiple of the number of pages
1428 * and the byte stride.
1429 * This is used internally, to ensure buffers start on page
1430 * boundaries, for the benefit of PVR Services.
1431 */
1432 psPVRFBInfo->ulRoundedBufferSize = RoundUpToMultiple(psPVRFBInfo->ulBufferSize, ulLCM);
1433 }
1434
1435 if(psLINFBInfo->var.bits_per_pixel == 16)
1436 {
1437 if((psLINFBInfo->var.red.length == 5) &&
1438 (psLINFBInfo->var.green.length == 6) &&
1439 (psLINFBInfo->var.blue.length == 5) &&
1440 (psLINFBInfo->var.red.offset == 11) &&
1441 (psLINFBInfo->var.green.offset == 5) &&
1442 (psLINFBInfo->var.blue.offset == 0) &&
1443 (psLINFBInfo->var.red.msb_right == 0))
1444 {
1445 psPVRFBInfo->ePixelFormat = PVRSRV_PIXEL_FORMAT_RGB565;
1446 }
1447 else
1448 {
1449 printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID);
1450 }
1451 }
1452 else if(psLINFBInfo->var.bits_per_pixel == 32)
1453 {
1454 if((psLINFBInfo->var.red.length == 8) &&
1455 (psLINFBInfo->var.green.length == 8) &&
1456 (psLINFBInfo->var.blue.length == 8) &&
1457 (psLINFBInfo->var.red.offset == 16) &&
1458 (psLINFBInfo->var.green.offset == 8) &&
1459 (psLINFBInfo->var.blue.offset == 0) &&
1460 (psLINFBInfo->var.red.msb_right == 0))
1461 {
1462 psPVRFBInfo->ePixelFormat = PVRSRV_PIXEL_FORMAT_ARGB8888;
1463 }
1464 else
1465 {
1466 printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID);
1467 }
1468 }
1469 else
1470 {
1471 printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID);
1472 }
1473
1474 psDevInfo->sFBInfo.ulPhysicalWidthmm =
1475 ((int)psLINFBInfo->var.width > 0) ? psLINFBInfo->var.width : 90;
1476
1477 psDevInfo->sFBInfo.ulPhysicalHeightmm =
1478 ((int)psLINFBInfo->var.height > 0) ? psLINFBInfo->var.height : 54;
1479
1480 /* System Surface */
1481 psDevInfo->sFBInfo.sSysAddr.uiAddr = psPVRFBInfo->sSysAddr.uiAddr;
1482 psDevInfo->sFBInfo.sCPUVAddr = psPVRFBInfo->sCPUVAddr;
1483
1484 eError = DC_SUNXI_OK;
1485 goto ErrorRelSem;
1486
1487ErrorFBRel:
1488 if (psLINFBInfo->fbops->fb_release != NULL)
1489 {
1490 (void) psLINFBInfo->fbops->fb_release(psLINFBInfo, 0);
1491 }
1492ErrorModPut:
1493 module_put(psLINFBOwner);
1494ErrorRelSem:
1495 console_unlock();
1496
1497 return eError;
1498}
1499
1500static void DC_SUNXIDeInitFBDev(DC_SUNXI_DEVINFO *psDevInfo)
1501{
1502 struct fb_info *psLINFBInfo = psDevInfo->psLINFBInfo;
1503 struct module *psLINFBOwner;
1504
1505 console_lock();
1506
1507 psLINFBOwner = psLINFBInfo->fbops->owner;
1508
1509 if (psLINFBInfo->fbops->fb_release != NULL)
1510 {
1511 (void) psLINFBInfo->fbops->fb_release(psLINFBInfo, 0);
1512 }
1513
1514 module_put(psLINFBOwner);
1515
1516 console_unlock();
1517}
1518
1519static DC_SUNXI_DEVINFO *DC_SUNXIInitDev(unsigned uiFBDevID)
1520{
1521 PFN_CMD_PROC pfnCmdProcList[DC_SUNXI_COMMAND_COUNT];
1522 IMG_UINT32 aui32SyncCountList[DC_SUNXI_COMMAND_COUNT][2];
1523 DC_SUNXI_DEVINFO *psDevInfo = NULL;
1524
1525 /* Allocate device info. structure */
1526 psDevInfo = (DC_SUNXI_DEVINFO *)kmalloc(sizeof(DC_SUNXI_DEVINFO), GFP_KERNEL);
1527
1528 if(psDevInfo == NULL)
1529 {
1530 printk(KERN_ERR DRIVER_PREFIX
1531 ": %s: Device %u: Couldn't allocate device information structure\n", __FUNCTION__, uiFBDevID);
1532
1533 goto ErrorExit;
1534 }
1535
1536 /* Any fields not set will be zero */
1537 memset(psDevInfo, 0, sizeof(DC_SUNXI_DEVINFO));
1538
1539 psDevInfo->uiFBDevID = uiFBDevID;
1540
1541 /* Get the kernel services function table */
1542 if(!(*gpfnGetPVRJTable)(&psDevInfo->sPVRJTable))
1543 {
1544 goto ErrorFreeDevInfo;
1545 }
1546
1547 /* Save private fbdev information structure in the dev. info. */
1548 if(DC_SUNXIInitFBDev(psDevInfo) != DC_SUNXI_OK)
1549 {
1550 /*
1551 * Leave it to DC_SUNXIInitFBDev to print an error message, if
1552 * required. The function may have failed because
1553 * there is no Linux framebuffer device corresponding
1554 * to the device ID.
1555 */
1556 goto ErrorIonClientDestroy;
1557 }
1558
1559 psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers = (IMG_UINT32)(psDevInfo->sFBInfo.ulFBSize / psDevInfo->sFBInfo.ulRoundedBufferSize);
1560 if (psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers != 0)
1561 {
1562 psDevInfo->sDisplayInfo.ui32MaxSwapChains = 1;
1563 psDevInfo->sDisplayInfo.ui32MaxSwapInterval = 1;
1564 }
1565
1566 psDevInfo->sDisplayInfo.ui32PhysicalWidthmm = psDevInfo->sFBInfo.ulPhysicalWidthmm;
1567 psDevInfo->sDisplayInfo.ui32PhysicalHeightmm = psDevInfo->sFBInfo.ulPhysicalHeightmm;
1568
1569 strncpy(psDevInfo->sDisplayInfo.szDisplayName, DISPLAY_DEVICE_NAME, MAX_DISPLAY_NAME_SIZE);
1570
1571 psDevInfo->sDisplayFormat.pixelformat = psDevInfo->sFBInfo.ePixelFormat;
1572 psDevInfo->sDisplayDim.ui32Width = (IMG_UINT32)psDevInfo->sFBInfo.ulWidth;
1573 psDevInfo->sDisplayDim.ui32Height = (IMG_UINT32)psDevInfo->sFBInfo.ulHeight;
1574 psDevInfo->sDisplayDim.ui32ByteStride = (IMG_UINT32)psDevInfo->sFBInfo.ulByteStride;
1575
1576 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX
1577 ": Device %u: Maximum number of swap chain buffers: %u\n",
1578 psDevInfo->uiFBDevID, psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers));
1579
1580 /* Setup system buffer */
1581 psDevInfo->sSystemBuffer.sSysAddr = psDevInfo->sFBInfo.sSysAddr;
1582 psDevInfo->sSystemBuffer.sCPUVAddr = psDevInfo->sFBInfo.sCPUVAddr;
1583 psDevInfo->sSystemBuffer.psDevInfo = psDevInfo;
1584
1585 DC_SUNXIInitBufferForSwap(&psDevInfo->sSystemBuffer);
1586
1587 /*
1588 * Setup the DC Jtable so SRVKM can call into this driver
1589 */
1590 psDevInfo->sDCJTable.ui32TableSize = sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE);
1591 psDevInfo->sDCJTable.pfnOpenDCDevice = OpenDCDevice;
1592 psDevInfo->sDCJTable.pfnCloseDCDevice = CloseDCDevice;
1593 psDevInfo->sDCJTable.pfnEnumDCFormats = EnumDCFormats;
1594 psDevInfo->sDCJTable.pfnEnumDCDims = EnumDCDims;
1595 psDevInfo->sDCJTable.pfnGetDCSystemBuffer = GetDCSystemBuffer;
1596 psDevInfo->sDCJTable.pfnGetDCInfo = GetDCInfo;
1597 psDevInfo->sDCJTable.pfnGetBufferAddr = GetDCBufferAddr;
1598 psDevInfo->sDCJTable.pfnCreateDCSwapChain = CreateDCSwapChain;
1599 psDevInfo->sDCJTable.pfnDestroyDCSwapChain = DestroyDCSwapChain;
1600 psDevInfo->sDCJTable.pfnSetDCDstRect = SetDCDstRect;
1601 psDevInfo->sDCJTable.pfnSetDCSrcRect = SetDCSrcRect;
1602 psDevInfo->sDCJTable.pfnSetDCDstColourKey = SetDCDstColourKey;
1603 psDevInfo->sDCJTable.pfnSetDCSrcColourKey = SetDCSrcColourKey;
1604 psDevInfo->sDCJTable.pfnGetDCBuffers = GetDCBuffers;
1605 psDevInfo->sDCJTable.pfnSwapToDCBuffer = SwapToDCBuffer;
1606 psDevInfo->sDCJTable.pfnSetDCState = SetDCState;
1607
1608 /* Register device with services and retrieve device index */
1609 if(psDevInfo->sPVRJTable.pfnPVRSRVRegisterDCDevice(
1610 &psDevInfo->sDCJTable,
1611 &psDevInfo->uiPVRDevID) != PVRSRV_OK)
1612 {
1613 printk(KERN_ERR DRIVER_PREFIX
1614 ": %s: Device %u: PVR Services device registration failed\n", __FUNCTION__, uiFBDevID);
1615
1616 goto ErrorDeInitFBDev;
1617 }
1618
1619 DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: PVR Device ID: %u\n",
1620 psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID));
1621
1622 /* Setup private command processing function table ... */
1623 pfnCmdProcList[DC_FLIP_COMMAND] = ProcessFlip;
1624
1625 /* ... and associated sync count(s) */
1626 aui32SyncCountList[DC_FLIP_COMMAND][0] = 0; /* writes */
1627 aui32SyncCountList[DC_FLIP_COMMAND][1] = 10; /* reads */
1628
1629 if (psDevInfo->sPVRJTable.pfnPVRSRVRegisterCmdProcList(psDevInfo->uiPVRDevID,
1630 &pfnCmdProcList[0],
1631 aui32SyncCountList,
1632 DC_SUNXI_COMMAND_COUNT) != PVRSRV_OK)
1633 {
1634 printk(KERN_ERR DRIVER_PREFIX
1635 ": %s: Device %u: Couldn't register command processing functions with PVR Services\n", __FUNCTION__, uiFBDevID);
1636 goto ErrorUnregisterDevice;
1637 }
1638
1639 mutex_init(&psDevInfo->sCreateSwapChainMutex);
1640
1641 atomic_set(&psDevInfo->sBlankEvents, 0);
1642 atomic_set(&psDevInfo->sFlushCommands, false);
1643
1644 return psDevInfo;
1645
1646ErrorUnregisterDevice:
1647 (void)psDevInfo->sPVRJTable.pfnPVRSRVRemoveDCDevice(psDevInfo->uiPVRDevID);
1648ErrorDeInitFBDev:
1649 DC_SUNXIDeInitFBDev(psDevInfo);
1650ErrorIonClientDestroy:
1651ErrorFreeDevInfo:
1652 kfree(psDevInfo);
1653ErrorExit:
1654 return NULL;
1655}
1656
1657static DC_SUNXI_ERROR DC_SUNXIInit(void)
1658{
1659 unsigned int i, uiMaxFBDevIDPlusOne = DC_SUNXIMaxFBDevIDPlusOne();
1660 unsigned int uiDevicesFound = 0;
1661
1662 gpfnGetPVRJTable = PVRGetDisplayClassJTable;
1663
1664 /*
1665 * We search for frame buffer devices backwards, as the last device
1666 * registered with PVR Services will be the first device enumerated
1667 * by PVR Services.
1668 */
1669 for(i = uiMaxFBDevIDPlusOne; i-- != 0;)
1670 {
1671 DC_SUNXI_DEVINFO *psDevInfo = DC_SUNXIInitDev(i);
1672
1673 if (psDevInfo != NULL)
1674 {
1675 /* Set the top-level anchor */
1676 DC_SUNXISetDevInfoPtr(psDevInfo->uiFBDevID, psDevInfo);
1677 uiDevicesFound++;
1678 }
1679 }
1680
1681 return (uiDevicesFound != 0) ? DC_SUNXI_OK : DC_SUNXI_ERROR_INIT_FAILURE;
1682}
1683
1684static bool DC_SUNXIDeInitDev(DC_SUNXI_DEVINFO *psDevInfo)
1685{
1686 PVRSRV_DC_DISP2SRV_KMJTABLE *psPVRJTable = &psDevInfo->sPVRJTable;
1687
1688 if (psPVRJTable->pfnPVRSRVRemoveCmdProcList (psDevInfo->uiPVRDevID, DC_SUNXI_COMMAND_COUNT) != PVRSRV_OK)
1689 {
1690 printk(KERN_ERR DRIVER_PREFIX
1691 ": %s: Device %u: PVR Device %u: Couldn't unregister command processing functions\n", __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID);
1692 return false;
1693 }
1694
1695 if (psPVRJTable->pfnPVRSRVRemoveDCDevice(psDevInfo->uiPVRDevID) != PVRSRV_OK)
1696 {
1697 printk(KERN_ERR DRIVER_PREFIX
1698 ": %s: Device %u: PVR Device %u: Couldn't remove device from PVR Services\n", __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID);
1699 return false;
1700 }
1701
1702 mutex_destroy(&psDevInfo->sCreateSwapChainMutex);
1703
1704 DC_SUNXIDeInitFBDev(psDevInfo);
1705
1706 DC_SUNXISetDevInfoPtr(psDevInfo->uiFBDevID, NULL);
1707
1708 /* De-allocate data structure */
1709 kfree(psDevInfo);
1710
1711 return true;
1712}
1713
1714static DC_SUNXI_ERROR DC_SUNXIDeInit(void)
1715{
1716 unsigned int i, uiMaxFBDevIDPlusOne = DC_SUNXIMaxFBDevIDPlusOne();
1717 bool bError = false;
1718
1719 for(i = 0; i < uiMaxFBDevIDPlusOne; i++)
1720 {
1721 DC_SUNXI_DEVINFO *psDevInfo = DC_SUNXIGetDevInfoPtr(i);
1722
1723 if (psDevInfo != NULL)
1724 {
1725 bError |= !DC_SUNXIDeInitDev(psDevInfo);
1726 }
1727 }
1728
1729 return (bError) ? DC_SUNXI_ERROR_INIT_FAILURE : DC_SUNXI_OK;
1730}
1731
1732#if defined(SUPPORT_DRI_DRM)
1733int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Init)(struct drm_device unref__ *dev)
1734#else
1735static int __init DC_SUNXI_Init(void)
1736#endif
1737{
1738 if(DC_SUNXIInit() != DC_SUNXI_OK)
1739 {
1740 printk(KERN_ERR DRIVER_PREFIX ": %s: DC_SUNXIInit failed\n", __FUNCTION__);
1741 return -ENODEV;
1742 }
1743
1744 return 0;
1745}
1746
1747#if defined(SUPPORT_DRI_DRM)
1748void PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Cleanup)(struct drm_device unref__ *dev)
1749#else
1750static void __exit DC_SUNXI_Cleanup(void)
1751#endif
1752{
1753 if(DC_SUNXIDeInit() != DC_SUNXI_OK)
1754 {
1755 printk(KERN_ERR DRIVER_PREFIX ": %s: DC_SUNXIDeInit failed\n", __FUNCTION__);
1756 }
1757}
1758
1759#if !defined(SUPPORT_DRI_DRM)
1760late_initcall(DC_SUNXI_Init);
1761module_exit(DC_SUNXI_Cleanup);
1762#endif
1763/******************************************************************************
1764 * End of file (dc_sunxi_displayclass.c)
1765 ******************************************************************************/