1 /*
2 * Allwinner SoCs display driver.
3 *
4 * Copyright (C) 2016 Allwinner.
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11 #include "disp_private.h"
12 #include "../dev_disp.h"
13 #include "../disp_trace.h"
14
15 extern struct disp_drv_info g_disp_drv;
16
17 #if defined(SUPPORT_EINK) && defined(CONFIG_EINK_PANEL_USED)
18
disp_delay_ms(u32 ms)19 s32 disp_delay_ms(u32 ms)
20 {
21 #if defined(__LINUX_PLAT__)
22 mdelay(ms);
23 #endif
24 #ifdef __UBOOT_PLAT__
25 __msdelay(ms);
26 #endif
27 return 0;
28 }
29
30 #else
31
disp_delay_ms(u32 ms)32 s32 disp_delay_ms(u32 ms)
33 {
34 #if defined(__LINUX_PLAT__)
35 u32 timeout = msecs_to_jiffies(ms);
36
37 set_current_state(TASK_UNINTERRUPTIBLE);
38 schedule_timeout(timeout);
39 #endif
40 #ifdef __UBOOT_PLAT__
41 __msdelay(ms);
42 #endif
43 return 0;
44 }
45
46 #endif /*endif SUPPORT_EINK */
47
disp_delay_us(u32 us)48 s32 disp_delay_us(u32 us)
49 {
50 udelay(us);
51
52 return 0;
53 }
54
dump_layer_config(struct disp_layer_config_data * data)55 u32 dump_layer_config(struct disp_layer_config_data *data)
56 {
57 u32 count = 0;
58 char buf[512];
59
60 count +=
61 sprintf(buf + count, " %6s ",
62 (data->config.info.mode == LAYER_MODE_BUFFER) ?
63 "buffer" : "color");
64 count +=
65 sprintf(buf + count, " %8s ",
66 (data->config.enable == 1) ? "enable" : "disable");
67 count += sprintf(buf + count, "ch[%1d] ", data->config.channel);
68 count += sprintf(buf + count, "lyr[%1d] ", data->config.layer_id);
69 count += sprintf(buf + count, "z[%1d] ", data->config.info.zorder);
70 count +=
71 sprintf(buf + count, "pre_m[%1s] ",
72 (data->config.info.fb.pre_multiply) ? "Y" : "N");
73 count +=
74 sprintf(buf + count, "alpha[%5s %3d] ",
75 (data->config.info.alpha_mode) ? "globl" : "pixel",
76 data->config.info.alpha_value);
77 count += sprintf(buf + count, "fmt[%3d] ", data->config.info.fb.format);
78 count +=
79 sprintf(buf + count, "size[%4d,%4d;%4d,%4d;%4d,%4d] ",
80 data->config.info.fb.size[0].width,
81 data->config.info.fb.size[0].height,
82 data->config.info.fb.size[0].width,
83 data->config.info.fb.size[0].height,
84 data->config.info.fb.size[0].width,
85 data->config.info.fb.size[0].height);
86 count +=
87 sprintf(buf + count, "crop[%4d,%4d,%4d,%4d] ",
88 (u32) (data->config.info.fb.crop.x >> 32),
89 (u32) (data->config.info.fb.crop.y >> 32),
90 (u32) (data->config.info.fb.crop.width >> 32),
91 (u32) (data->config.info.fb.crop.height >> 32));
92 count +=
93 sprintf(buf + count, "frame[%4d,%4d,%4d,%4d] ",
94 data->config.info.screen_win.x,
95 data->config.info.screen_win.y,
96 data->config.info.screen_win.width,
97 data->config.info.screen_win.height);
98 count +=
99 sprintf(buf + count, "addr[%8llx,%8llx,%8llx] ",
100 data->config.info.fb.addr[0], data->config.info.fb.addr[1],
101 data->config.info.fb.addr[2]);
102 count += sprintf(buf + count, "flag[0x%8x] ", data->flag);
103 count += sprintf(buf + count, "\n");
104
105 DE_WRN("%s", buf);
106 return count;
107 }
108
disp_vmap(unsigned long phys_addr,unsigned long size)109 void *disp_vmap(unsigned long phys_addr, unsigned long size)
110 {
111 int npages = PAGE_ALIGN(size) / PAGE_SIZE;
112 struct page **pages = vmalloc(sizeof(struct page *) * npages);
113 struct page **tmp = pages;
114 struct page *cur_page = phys_to_page(phys_addr);
115 pgprot_t pgprot;
116 void *vaddr = NULL;
117 int i;
118
119 if (!pages)
120 return NULL;
121
122 for (i = 0; i < npages; i++)
123 *(tmp++) = cur_page++;
124
125 pgprot = PAGE_KERNEL;
126 vaddr = vmap(pages, npages, VM_MAP, pgprot);
127
128 vfree(pages);
129 return vaddr;
130 }
131
disp_vunmap(const void * vaddr)132 void disp_vunmap(const void *vaddr)
133 {
134 vunmap(vaddr);
135 }
136
disp_dma_map_core(int fd,struct dmabuf_item * item)137 static int disp_dma_map_core(int fd, struct dmabuf_item *item)
138 {
139 #if defined(__LINUX_PLAT__)
140 struct dma_buf *dmabuf;
141 struct dma_buf_attachment *attachment;
142 struct sg_table *sgt;
143 int ret = -1;
144
145 if (fd < 0) {
146 DE_WRN("dma_buf_id(%d) is invalid\n", fd);
147 goto exit;
148 }
149 dmabuf = dma_buf_get(fd);
150 if (IS_ERR(dmabuf)) {
151 DE_WRN("dma_buf_get failed, fd=%d\n", fd);
152 goto exit;
153 }
154 attachment = dma_buf_attach(dmabuf, g_disp_drv.dev);
155 if (IS_ERR(attachment)) {
156 DE_WRN("dma_buf_attach failed\n");
157 goto err_buf_put;
158 }
159 sgt = dma_buf_map_attachment(attachment, DMA_FROM_DEVICE);
160 if (IS_ERR_OR_NULL(sgt)) {
161 DE_WRN("dma_buf_map_attachment failed\n");
162 goto err_buf_detach;
163 }
164
165 item->dmabuf = dmabuf;
166 item->sgt = sgt;
167 item->attachment = attachment;
168 item->dma_addr = sg_dma_address(sgt->sgl);
169 ret = 0;
170
171 goto exit;
172
173 /* unmap attachment sgt, not sgt_bak, cause it's not alloc yet! */
174 dma_buf_unmap_attachment(attachment, sgt, DMA_FROM_DEVICE);
175 err_buf_detach:
176 dma_buf_detach(dmabuf, attachment);
177 err_buf_put:
178 dma_buf_put(dmabuf);
179 exit:
180 return ret;
181 #endif
182 return 0;
183 }
184
disp_dma_unmap_core(struct dmabuf_item * item)185 static void disp_dma_unmap_core(struct dmabuf_item *item)
186 {
187 #if defined(__LINUX_PLAT__)
188 dma_buf_unmap_attachment(item->attachment, item->sgt, DMA_FROM_DEVICE);
189 dma_buf_detach(item->dmabuf, item->attachment);
190 dma_buf_put(item->dmabuf);
191 #endif
192 }
193
disp_dma_map(int fd)194 struct dmabuf_item *disp_dma_map(int fd)
195 {
196 #if defined(__LINUX_PLAT__)
197 struct dmabuf_item *item = NULL;
198
199 DISP_TRACE_BEGIN("disp_dma_map");
200 item = kmalloc(sizeof(struct dmabuf_item),
201 GFP_KERNEL | __GFP_ZERO);
202
203 if (item == NULL) {
204 DE_WRN("malloc memory of size %d fail!\n",
205 (unsigned int)sizeof(struct dmabuf_item));
206 goto exit;
207 }
208 if (disp_dma_map_core(fd, item) != 0) {
209 kfree(item);
210 item = NULL;
211 }
212
213 exit:
214 DISP_TRACE_END("disp_dma_map");
215 return item;
216 #else
217
218 return NULL;
219 #endif
220 }
221
disp_dma_unmap(struct dmabuf_item * item)222 void disp_dma_unmap(struct dmabuf_item *item)
223 {
224 #if defined(__LINUX_PLAT__)
225 DISP_TRACE_BEGIN("disp_dma_unmap");
226 disp_dma_unmap_core(item);
227 kfree(item);
228 DISP_TRACE_END("disp_dma_unmap");
229 #endif
230 }
231
232 static struct disp_format_attr fmt_attr_tbl[] = {
233 /* format bits */
234 /* hor_rsample(u,v) */
235 /* ver_rsample(u,v) */
236 /* uvc */
237 /* interleave */
238 /* factor */
239 /* div */
240
241 { DISP_FORMAT_ARGB_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
242 { DISP_FORMAT_ABGR_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
243 { DISP_FORMAT_RGBA_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
244 { DISP_FORMAT_BGRA_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
245 { DISP_FORMAT_XRGB_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
246 { DISP_FORMAT_XBGR_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
247 { DISP_FORMAT_RGBX_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
248 { DISP_FORMAT_BGRX_8888, 8, 1, 1, 1, 1, 0, 1, 4, 1},
249 { DISP_FORMAT_RGB_888, 8, 1, 1, 1, 1, 0, 1, 3, 1},
250 { DISP_FORMAT_BGR_888, 8, 1, 1, 1, 1, 0, 1, 3, 1},
251 { DISP_FORMAT_RGB_565, 8, 1, 1, 1, 1, 0, 1, 2, 1},
252 { DISP_FORMAT_BGR_565, 8, 1, 1, 1, 1, 0, 1, 2, 1},
253 { DISP_FORMAT_ARGB_4444, 8, 1, 1, 1, 1, 0, 1, 2, 1},
254 { DISP_FORMAT_ABGR_4444, 8, 1, 1, 1, 1, 0, 1, 2, 1},
255 { DISP_FORMAT_RGBA_4444, 8, 1, 1, 1, 1, 0, 1, 2, 1},
256 { DISP_FORMAT_BGRA_4444, 8, 1, 1, 1, 1, 0, 1, 2, 1},
257 { DISP_FORMAT_ARGB_1555, 8, 1, 1, 1, 1, 0, 1, 2, 1},
258 { DISP_FORMAT_ABGR_1555, 8, 1, 1, 1, 1, 0, 1, 2, 1},
259 { DISP_FORMAT_RGBA_5551, 8, 1, 1, 1, 1, 0, 1, 2, 1},
260 { DISP_FORMAT_BGRA_5551, 8, 1, 1, 1, 1, 0, 1, 2, 1},
261 { DISP_FORMAT_A2R10G10B10, 10, 1, 1, 1, 1, 0, 1, 4, 1},
262 { DISP_FORMAT_A2B10G10R10, 10, 1, 1, 1, 1, 0, 1, 4, 1},
263 { DISP_FORMAT_R10G10B10A2, 10, 1, 1, 1, 1, 0, 1, 4, 1},
264 { DISP_FORMAT_B10G10R10A2, 10, 1, 1, 1, 1, 0, 1, 4, 1},
265 { DISP_FORMAT_YUV444_I_AYUV, 8, 1, 1, 1, 1, 0, 1, 3, 1},
266 { DISP_FORMAT_YUV444_I_VUYA, 8, 1, 1, 1, 1, 0, 1, 3, 1},
267 { DISP_FORMAT_YUV422_I_YVYU, 8, 1, 1, 1, 1, 0, 1, 2, 1},
268 { DISP_FORMAT_YUV422_I_YUYV, 8, 1, 1, 1, 1, 0, 1, 2, 1},
269 { DISP_FORMAT_YUV422_I_UYVY, 8, 1, 1, 1, 1, 0, 1, 2, 1},
270 { DISP_FORMAT_YUV422_I_VYUY, 8, 1, 1, 1, 1, 0, 1, 2, 1},
271 { DISP_FORMAT_YUV444_P, 8, 1, 1, 1, 1, 0, 1, 1, 1},
272 { DISP_FORMAT_YUV422_P, 8, 2, 2, 1, 1, 0, 0, 2, 1},
273 { DISP_FORMAT_YUV420_P, 8, 2, 2, 2, 2, 0, 0, 3, 2},
274 { DISP_FORMAT_YUV411_P, 8, 4, 4, 1, 1, 0, 0, 3, 2},
275 { DISP_FORMAT_YUV422_SP_UVUV, 8, 2, 2, 1, 1, 1, 0, 2, 1},
276 { DISP_FORMAT_YUV422_SP_VUVU, 8, 2, 2, 1, 1, 1, 0, 2, 1},
277 { DISP_FORMAT_YUV420_SP_UVUV, 8, 2, 2, 2, 2, 1, 0, 3, 2},
278 { DISP_FORMAT_YUV420_SP_VUVU, 8, 2, 2, 2, 2, 1, 0, 3, 2},
279 { DISP_FORMAT_YUV411_SP_UVUV, 8, 4, 4, 1, 1, 1, 0, 3, 2},
280 { DISP_FORMAT_YUV411_SP_VUVU, 8, 4, 4, 1, 1, 1, 0, 3, 2},
281 { DISP_FORMAT_8BIT_GRAY, 8, 1, 1, 1, 1, 0, 0, 1, 1},
282 { DISP_FORMAT_YUV444_I_AYUV_10BIT, 10, 1, 1, 1, 1, 0, 1, 4, 1},
283 { DISP_FORMAT_YUV444_I_VUYA_10BIT, 10, 1, 1, 1, 1, 0, 1, 4, 1},
284 { DISP_FORMAT_YUV422_I_YVYU_10BIT, 10, 1, 1, 1, 1, 0, 1, 3, 1},
285 { DISP_FORMAT_YUV422_I_YUYV_10BIT, 10, 1, 1, 1, 1, 0, 1, 3, 1},
286 { DISP_FORMAT_YUV422_I_UYVY_10BIT, 10, 1, 1, 1, 1, 0, 1, 3, 1},
287 { DISP_FORMAT_YUV422_I_VYUY_10BIT, 10, 1, 1, 1, 1, 0, 1, 3, 1},
288 { DISP_FORMAT_YUV444_P_10BIT, 10, 1, 1, 1, 1, 0, 0, 6, 1},
289 { DISP_FORMAT_YUV422_P_10BIT, 10, 2, 2, 1, 1, 0, 0, 4, 1},
290 { DISP_FORMAT_YUV420_P_10BIT, 10, 2, 2, 2, 2, 0, 0, 3, 1},
291 { DISP_FORMAT_YUV411_P_10BIT, 10, 4, 4, 1, 1, 0, 0, 3, 1},
292 { DISP_FORMAT_YUV422_SP_UVUV_10BIT, 10, 2, 2, 1, 1, 1, 0, 4, 1},
293 { DISP_FORMAT_YUV422_SP_VUVU_10BIT, 10, 2, 2, 1, 1, 1, 0, 4, 1},
294 { DISP_FORMAT_YUV420_SP_UVUV_10BIT, 10, 2, 2, 2, 2, 1, 0, 3, 1},
295 { DISP_FORMAT_YUV420_SP_VUVU_10BIT, 10, 2, 2, 2, 2, 1, 0, 3, 1},
296 { DISP_FORMAT_YUV411_SP_UVUV_10BIT, 10, 4, 4, 1, 1, 1, 0, 3, 1},
297 { DISP_FORMAT_YUV411_SP_VUVU_10BIT, 10, 4, 4, 1, 1, 1, 0, 3, 1},
298 };
299
300 /* @left_eye: indicate left eye buffer when true */
disp_set_fb_info(struct fb_address_transfer * fb,bool left_eye)301 s32 disp_set_fb_info(struct fb_address_transfer *fb, bool left_eye)
302 {
303 s32 ret = -1;
304 u32 i = 0;
305 u32 len = ARRAY_SIZE(fmt_attr_tbl);
306 u32 y_width, y_height, u_width, u_height;
307 u32 y_pitch, u_pitch;
308 u32 y_size, u_size;
309 unsigned long long fb_addr[3];
310
311
312 y_width = fb->size[0].width;
313 y_height = fb->size[0].height;
314 u_width = fb->size[1].width;
315 u_height = fb->size[1].height;
316 u_width = (u_width == 0) ? y_width : u_width;
317 u_height = (u_height == 0) ? y_height : u_height;
318
319 fb_addr[0] = fb->dma_addr;
320
321 if (fb->format >= DISP_FORMAT_MAX) {
322 DE_WRN("%s, format 0x%x is out of range\n", __func__,
323 fb->format);
324 goto exit;
325 }
326
327 for (i = 0; i < len; ++i) {
328
329 if (fmt_attr_tbl[i].format == fb->format) {
330 y_pitch = y_width *
331 ((fmt_attr_tbl[i].bits == 8) ? 1 : 2);
332 u_pitch = u_width *
333 ((fmt_attr_tbl[i].bits == 8) ? 1 : 2) *
334 (fmt_attr_tbl[i].uvc + 1);
335
336 y_pitch = DISPALIGN(y_pitch, fb->align[0]);
337 u_pitch = DISPALIGN(u_pitch, fb->align[1]);
338 y_size = y_pitch * y_height;
339 u_size = u_pitch * u_height;
340
341 fb_addr[1] = fb->dma_addr + y_size; /* u */
342 fb_addr[2] = fb->dma_addr + y_size + u_size; /* v */
343
344 if (fb->format == DISP_FORMAT_YUV420_P ||
345 fb->format == DISP_FORMAT_YUV420_P_10BIT) {
346 /* v */
347 fb_addr[1] = fb->dma_addr + y_size + u_size;
348 fb_addr[2] = fb->dma_addr + y_size; /* u */
349 }
350 ret = 0;
351 break;
352 }
353 }
354 if (fb->format >= DISP_FORMAT_1bpp_palette_LE && fb->format <= DISP_FORMAT_8bpp_palette_LE) {
355 fb_addr[0] = fb->dma_addr;
356 fb_addr[1] = fb->dma_addr;
357 fb_addr[2] = fb->dma_addr;
358 ret = 0;
359 }
360 if (ret != 0) {
361 DE_WRN("%s, format 0x%x is invalid\n", __func__,
362 fb->format);
363 } else {
364 if (left_eye) {
365 fb->addr[0] = fb_addr[0];
366 fb->addr[1] = fb_addr[1];
367 fb->addr[2] = fb_addr[2];
368 fb->trd_right_addr[0] = fb_addr[0];
369 fb->trd_right_addr[1] = fb_addr[1];
370 fb->trd_right_addr[2] = fb_addr[2];
371 } else {
372 fb->trd_right_addr[0] = fb_addr[0];
373 fb->trd_right_addr[1] = fb_addr[1];
374 fb->trd_right_addr[2] = fb_addr[2];
375 }
376 }
377 exit:
378 return ret;
379 }
380 EXPORT_SYMBOL(disp_set_fb_info);
381
disp_set_fb_base_on_depth(struct fb_address_transfer * fb)382 s32 disp_set_fb_base_on_depth(struct fb_address_transfer *fb)
383 {
384 s32 ret = -1;
385 u32 i = 0;
386 u32 len = ARRAY_SIZE(fmt_attr_tbl);
387 int depth = fb->depth;
388 unsigned long long abs_depth = (depth > 0) ?
389 depth : (-depth);
390 /*
391 * 1: left & right move closer, right buffer address move right
392 * 0: in opposite direction
393 */
394 unsigned int direction = (depth > 0) ? 1 : 0;
395 unsigned int offset = 0;
396
397 if (fb->format >= DISP_FORMAT_MAX) {
398 DE_WRN("%s, format 0x%x is out of range\n", __func__,
399 fb->format);
400 goto exit;
401 }
402
403 for (i = 0; i < len; ++i) {
404 if (fmt_attr_tbl[i].format == fb->format) {
405 offset = fmt_attr_tbl[i].factor / fmt_attr_tbl[i].div;
406 offset = offset * (unsigned int)abs_depth;
407
408 ret = 0;
409 break;
410 }
411 }
412 if (ret != 0) {
413 DE_WRN("%s, format 0x%x is invalid\n", __func__,
414 fb->format);
415 } else {
416 if (direction == 0) {
417 fb->addr[0] += offset;
418 fb->addr[1] += offset;
419 fb->addr[2] += offset;
420 } else {
421 fb->trd_right_addr[0] += offset;
422 fb->trd_right_addr[1] += offset;
423 fb->trd_right_addr[2] += offset;
424 }
425 }
426 exit:
427 return ret;
428 }
429
430 /* *********************** disp irq util begin ********************* */
431 struct disp_irq_util {
432 s32 num;
433 u32 irq_no;
434 u32 irq_en;
435 struct disp_irq_info *irq_info[DISP_SCREEN_NUM + DISP_WB_NUM + DISP_SCREEN_NUM];
436 };
437
438 static struct disp_irq_util s_irq_util;
439 static DEFINE_SPINLOCK(s_irq_lock);
440
disp_init_irq_util(u32 irq_no)441 s32 disp_init_irq_util(u32 irq_no)
442 {
443 s_irq_util.irq_no = irq_no;
444 return 0;
445 }
446
disp_irq_handler(int irq,void * parg)447 static s32 disp_irq_handler(int irq, void *parg)
448 {
449 unsigned long flags;
450 const u32 total_num = sizeof(s_irq_util.irq_info)
451 / sizeof(s_irq_util.irq_info[0]);
452 u32 id;
453
454 for (id = 0; id < total_num; id++) {
455 struct disp_irq_info *irq_info;
456
457 spin_lock_irqsave(&s_irq_lock, flags);
458 irq_info = s_irq_util.irq_info[id];
459 spin_unlock_irqrestore(&s_irq_lock, flags);
460
461 if (irq_info)
462 irq_info->irq_handler(irq_info->sel,
463 irq_info->irq_flag, irq_info->ptr);
464 }
465
466 return DISP_IRQ_RETURN;
467 }
468
disp_register_irq(u32 id,struct disp_irq_info * irq_info)469 s32 disp_register_irq(u32 id, struct disp_irq_info *irq_info)
470 {
471 /*unsigned long flags;*/
472 const u32 max_id = sizeof(s_irq_util.irq_info)
473 / sizeof(s_irq_util.irq_info[0]);
474
475 if ((irq_info == NULL) || (id >= max_id)) {
476 __wrn("invalid pare: irq_info=%p, id=%d\n",
477 irq_info, id);
478 return -1;
479 }
480
481 __inf("id=%d, irq_info:sel=%d,irq_flag=0x%x\n", id,
482 irq_info->sel, irq_info->irq_flag);
483
484 /*spin_lock_irqsave(&s_irq_lock, flags);*/
485 if (s_irq_util.irq_info[id] == NULL) {
486 s_irq_util.irq_info[id] = irq_info;
487 s_irq_util.num++;
488 if ((s_irq_util.num > 0)
489 && (s_irq_util.irq_en == 0)) {
490 disp_sys_register_irq(s_irq_util.irq_no, 0,
491 disp_irq_handler, NULL, 0, 0);
492 disp_sys_enable_irq(s_irq_util.irq_no);
493 s_irq_util.irq_en = 1;
494 }
495 } else {
496 __wrn("irq for %d already registered\n", id);
497 }
498 /*spin_unlock_irqrestore(&s_irq_lock, flags);*/
499
500 return 0;
501 }
502
disp_unregister_irq(u32 id,struct disp_irq_info * irq_info)503 s32 disp_unregister_irq(u32 id, struct disp_irq_info *irq_info)
504 {
505 /*unsigned long flags;*/
506 const u32 max_id = sizeof(s_irq_util.irq_info)
507 / sizeof(s_irq_util.irq_info[0]);
508
509 if ((irq_info == NULL) || (id >= max_id)) {
510 __wrn("invalid pare: irq_info=%p, id=%d\n",
511 irq_info, id);
512 return -1;
513 }
514
515 /*spin_lock_irqsave(&s_irq_lock, flags);*/
516 if (s_irq_util.irq_info[id] == irq_info) {
517 s_irq_util.irq_info[id] = NULL;
518 s_irq_util.num--;
519 if ((s_irq_util.num == 0)
520 && (s_irq_util.irq_en != 0)) {
521 disp_sys_disable_irq(s_irq_util.irq_no);
522 disp_sys_unregister_irq(s_irq_util.irq_no,
523 disp_irq_handler, NULL);
524 s_irq_util.irq_en = 0;
525 }
526 } else {
527 __wrn("irq for %d already unregistered\n", id);
528 }
529 /*spin_unlock_irqrestore(&s_irq_lock, flags);*/
530 return 0;
531 }
532 /* *********************** disp irq util end ********************** */
533