PageRenderTime 78ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/video/omap2/dsscomp/base.c

https://bitbucket.org/slukk/jb-tsm-kernel-4.2
C | 504 lines | 370 code | 68 blank | 66 comment | 59 complexity | 94f2643902cd0cb0505d61bdcf5fc965 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1. /*
  2. * linux/drivers/video/omap2/dsscomp/base.c
  3. *
  4. * DSS Composition basic operation support
  5. *
  6. * Copyright (C) 2011 Texas Instruments, Inc
  7. * Author: Lajos Molnar <molnar@ti.com>
  8. *
  9. * This program is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License version 2 as published by
  11. * the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  16. * more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <linux/kernel.h>
  22. #include <linux/notifier.h>
  23. #include <mach/tiler.h>
  24. #include <video/omapdss.h>
  25. #include <video/dsscomp.h>
  26. #include <plat/dsscomp.h>
  27. #include "dsscomp.h"
  28. int debug;
  29. module_param(debug, int, 0644);
  30. /* color formats supported - bitfield info is used for truncation logic */
  31. static const struct color_info {
  32. int a_ix, a_bt; /* bitfields */
  33. int r_ix, r_bt;
  34. int g_ix, g_bt;
  35. int b_ix, b_bt;
  36. int x_bt;
  37. enum omap_color_mode mode;
  38. const char *name;
  39. } fmts[2][16] = { {
  40. { 0, 0, 0, 0, 0, 0, 0, 0, 1, OMAP_DSS_COLOR_CLUT1, "BITMAP1" },
  41. { 0, 0, 0, 0, 0, 0, 0, 0, 2, OMAP_DSS_COLOR_CLUT2, "BITMAP2" },
  42. { 0, 0, 0, 0, 0, 0, 0, 0, 4, OMAP_DSS_COLOR_CLUT4, "BITMAP4" },
  43. { 0, 0, 0, 0, 0, 0, 0, 0, 8, OMAP_DSS_COLOR_CLUT8, "BITMAP8" },
  44. { 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
  45. { 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
  46. { 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
  47. { 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
  48. "ARGB16-1555" },
  49. { 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
  50. { 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
  51. { 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
  52. { 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
  53. { 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
  54. { 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
  55. { 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
  56. { 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
  57. "xRGB15-1555" },
  58. }, {
  59. { 0, 0, 0, 0, 0, 0, 0, 0, 12, OMAP_DSS_COLOR_NV12, "NV12" },
  60. { 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
  61. { 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
  62. { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "invalid" },
  63. { 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
  64. { 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
  65. { 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
  66. { 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
  67. "ARGB16-1555" },
  68. { 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
  69. { 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
  70. { 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_YUV2, "YUYV" },
  71. { 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_UYVY, "UYVY" },
  72. { 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
  73. { 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
  74. { 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
  75. { 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
  76. "xRGB15-1555" },
  77. } };
  78. static const struct color_info *get_color_info(enum omap_color_mode mode)
  79. {
  80. int i;
  81. for (i = 0; i < sizeof(fmts) / sizeof(fmts[0][0]); i++)
  82. if (fmts[0][i].mode == mode)
  83. return fmts[0] + i;
  84. return NULL;
  85. }
  86. static int color_mode_to_bpp(enum omap_color_mode color_mode)
  87. {
  88. const struct color_info *ci = get_color_info(color_mode);
  89. BUG_ON(!ci);
  90. return ci->a_bt + ci->r_bt + ci->g_bt + ci->b_bt + ci->x_bt;
  91. }
  92. #ifdef CONFIG_DEBUG_FS
  93. const char *dsscomp_get_color_name(enum omap_color_mode m)
  94. {
  95. const struct color_info *ci = get_color_info(m);
  96. return ci ? ci->name : NULL;
  97. }
  98. #endif
  99. union rect {
  100. struct {
  101. s32 x;
  102. s32 y;
  103. s32 w;
  104. s32 h;
  105. };
  106. struct {
  107. s32 xy[2];
  108. s32 wh[2];
  109. };
  110. struct dss2_rect_t r;
  111. };
  112. int crop_to_rect(union rect *crop, union rect *win, union rect *vis,
  113. int rotation, int mirror)
  114. {
  115. int c, swap = rotation & 1;
  116. /* align crop window with display coordinates */
  117. if (swap)
  118. crop->y -= (crop->h = -crop->h);
  119. if (rotation & 2)
  120. crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
  121. if ((!mirror) ^ !(rotation & 2))
  122. crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
  123. for (c = 0; c < 2; c++) {
  124. /* see if complete buffer is outside the vis or it is
  125. fully cropped or scaled to 0 */
  126. if (win->wh[c] <= 0 || vis->wh[c] <= 0 ||
  127. win->xy[c] + win->wh[c] <= vis->xy[c] ||
  128. win->xy[c] >= vis->xy[c] + vis->wh[c] ||
  129. !crop->wh[c ^ swap])
  130. return -ENOENT;
  131. /* crop left/top */
  132. if (win->xy[c] < vis->xy[c]) {
  133. /* correction term */
  134. int a = (vis->xy[c] - win->xy[c]) *
  135. crop->wh[c ^ swap] / win->wh[c];
  136. crop->xy[c ^ swap] += a;
  137. crop->wh[c ^ swap] -= a;
  138. win->wh[c] -= vis->xy[c] - win->xy[c];
  139. win->xy[c] = vis->xy[c];
  140. }
  141. /* crop right/bottom */
  142. if (win->xy[c] + win->wh[c] > vis->xy[c] + vis->wh[c]) {
  143. crop->wh[c ^ swap] = crop->wh[c ^ swap] *
  144. (vis->xy[c] + vis->wh[c] - win->xy[c]) /
  145. win->wh[c];
  146. win->wh[c] = vis->xy[c] + vis->wh[c] - win->xy[c];
  147. }
  148. if (!crop->wh[c ^ swap] || !win->wh[c])
  149. return -ENOENT;
  150. }
  151. /* realign crop window to buffer coordinates */
  152. if (rotation & 2)
  153. crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
  154. if ((!mirror) ^ !(rotation & 2))
  155. crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
  156. if (swap)
  157. crop->y -= (crop->h = -crop->h);
  158. return 0;
  159. }
  160. int set_dss_ovl_info(struct dss2_ovl_info *oi)
  161. {
  162. struct omap_overlay_info info;
  163. struct omap_overlay *ovl;
  164. struct dss2_ovl_cfg *cfg;
  165. union rect crop, win, vis;
  166. int c;
  167. /* check overlay number */
  168. if (!oi || oi->cfg.ix >= omap_dss_get_num_overlays())
  169. return -EINVAL;
  170. cfg = &oi->cfg;
  171. ovl = omap_dss_get_overlay(cfg->ix);
  172. /* just in case there are new fields, we get the current info */
  173. ovl->get_overlay_info(ovl, &info);
  174. info.enabled = cfg->enabled;
  175. if (!cfg->enabled)
  176. goto done;
  177. /* copied params */
  178. info.zorder = cfg->zorder;
  179. if (cfg->zonly)
  180. goto done;
  181. info.global_alpha = cfg->global_alpha;
  182. info.pre_mult_alpha = cfg->pre_mult_alpha;
  183. info.rotation = cfg->rotation;
  184. info.mirror = cfg->mirror;
  185. info.color_mode = cfg->color_mode;
  186. /* crop to screen */
  187. crop.r = cfg->crop;
  188. win.r = cfg->win;
  189. vis.x = vis.y = 0;
  190. vis.w = ovl->manager->device->panel.timings.x_res;
  191. vis.h = ovl->manager->device->panel.timings.y_res;
  192. if (crop_to_rect(&crop, &win, &vis, cfg->rotation, cfg->mirror) ||
  193. vis.w < 2) {
  194. info.enabled = false;
  195. goto done;
  196. }
  197. /* adjust crop to UV pixel boundaries */
  198. for (c = 0; c < (cfg->color_mode == OMAP_DSS_COLOR_NV12 ? 2 :
  199. (cfg->color_mode &
  200. (OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) ? 1 : 0); c++) {
  201. /* keep the output window to avoid trembling edges */
  202. crop.wh[c] += crop.xy[c] & 1; /* round down start */
  203. crop.xy[c] &= ~1;
  204. crop.wh[c] += crop.wh[c] & 1; /* round up end */
  205. /*
  206. * Buffer is aligned on UV pixel boundaries, so no
  207. * worries about extending crop region.
  208. */
  209. }
  210. info.width = crop.w;
  211. info.height = crop.h;
  212. if (cfg->rotation & 1)
  213. /* DISPC uses swapped height/width for 90/270 degrees */
  214. swap(info.width, info.height);
  215. info.pos_x = win.x;
  216. info.pos_y = win.y;
  217. info.out_width = win.w;
  218. info.out_height = win.h;
  219. /* calculate addresses and cropping */
  220. info.paddr = oi->ba;
  221. info.p_uv_addr = (info.color_mode == OMAP_DSS_COLOR_NV12) ? oi->uv : 0;
  222. info.vaddr = NULL;
  223. /* check for TILER 2D buffer */
  224. if (info.paddr >= 0x60000000 && info.paddr < 0x78000000) {
  225. int bpp = 1 << ((info.paddr >> 27) & 3);
  226. struct tiler_view_t t;
  227. /* crop to top-left */
  228. /*
  229. * DSS supports YUV422 on 32-bit mode, but its technically
  230. * 2 bytes-per-pixel.
  231. * Also RGB24-888 is 3 bytes-per-pixel even though no
  232. * tiler pixel format matches this.
  233. */
  234. if (cfg->color_mode &
  235. (OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY))
  236. bpp = 2;
  237. else if (cfg->color_mode == OMAP_DSS_COLOR_RGB24P)
  238. bpp = 3;
  239. tilview_create(&t, info.paddr, cfg->width, cfg->height);
  240. info.paddr -= t.tsptr;
  241. tilview_crop(&t, 0, crop.y, cfg->width, crop.h);
  242. info.paddr += t.tsptr + bpp * crop.x;
  243. info.rotation_type = OMAP_DSS_ROT_TILER;
  244. info.screen_width = 0;
  245. /* for NV12 format also crop NV12 */
  246. if (info.color_mode == OMAP_DSS_COLOR_NV12) {
  247. tilview_create(&t, info.p_uv_addr,
  248. cfg->width >> 1, cfg->height >> 1);
  249. info.p_uv_addr -= t.tsptr;
  250. tilview_crop(&t, 0, crop.y >> 1, cfg->width >> 1,
  251. crop.h >> 1);
  252. info.p_uv_addr += t.tsptr + bpp * crop.x;
  253. }
  254. } else {
  255. /* program tiler 1D as SDMA */
  256. int bpp = color_mode_to_bpp(cfg->color_mode);
  257. info.screen_width = cfg->stride * 8 / (bpp == 12 ? 8 : bpp);
  258. info.paddr += crop.x * (bpp / 8) + crop.y * cfg->stride;
  259. /* for NV12 format also crop NV12 */
  260. if (info.color_mode == OMAP_DSS_COLOR_NV12)
  261. info.p_uv_addr += crop.x * (bpp / 8) +
  262. (crop.y >> 1) * cfg->stride;
  263. /* no rotation on DMA buffer */
  264. if (cfg->rotation & 3 || cfg->mirror)
  265. return -EINVAL;
  266. info.rotation_type = OMAP_DSS_ROT_DMA;
  267. }
  268. info.max_x_decim = cfg->decim.max_x ? : 255;
  269. info.max_y_decim = cfg->decim.max_y ? : 255;
  270. info.min_x_decim = cfg->decim.min_x ? : 1;
  271. info.min_y_decim = cfg->decim.min_y ? : 1;
  272. #if 0
  273. info.pic_height = cfg->height;
  274. info.field = 0;
  275. if (cfg->ilace & OMAP_DSS_ILACE_SEQ)
  276. info.field |= OMAP_FLAG_IBUF;
  277. if (cfg->ilace & OMAP_DSS_ILACE_SWAP)
  278. info.field |= OMAP_FLAG_ISWAP;
  279. /*
  280. * Ignore OMAP_DSS_ILACE as there is no real support yet for
  281. * interlaced interleaved vs progressive buffers
  282. */
  283. if (ovl->manager &&
  284. ovl->manager->device &&
  285. !strcmp(ovl->manager->device->name, "hdmi") &&
  286. is_hdmi_interlaced())
  287. info.field |= OMAP_FLAG_IDEV;
  288. info.out_wb = 0;
  289. #endif
  290. info.cconv = cfg->cconv;
  291. done:
  292. #if 0
  293. pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
  294. "mir=%d col=%x z=%d al=%02x prem=%d pich=%d ilace=%d\n",
  295. ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
  296. info.height, info.screen_width, info.out_width, info.out_height,
  297. info.pos_x, info.pos_y, info.rotation, info.mirror,
  298. info.color_mode, info.zorder, info.global_alpha,
  299. info.pre_mult_alpha, info.pic_height, info.field);
  300. #else
  301. pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
  302. "mir=%d col=%x z=%d al=%02x prem=%d\n",
  303. ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
  304. info.height, info.screen_width, info.out_width, info.out_height,
  305. info.pos_x, info.pos_y, info.rotation, info.mirror,
  306. info.color_mode, info.zorder, info.global_alpha,
  307. info.pre_mult_alpha);
  308. #endif
  309. /* set overlay info */
  310. return ovl->set_overlay_info(ovl, &info);
  311. }
  312. void swap_rb_in_ovl_info(struct dss2_ovl_info *oi)
  313. {
  314. /* we need to swap YUV color matrix if we are swapping R and B */
  315. if (oi->cfg.color_mode &
  316. (OMAP_DSS_COLOR_NV12 | OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) {
  317. swap(oi->cfg.cconv.ry, oi->cfg.cconv.by);
  318. swap(oi->cfg.cconv.rcr, oi->cfg.cconv.bcr);
  319. swap(oi->cfg.cconv.rcb, oi->cfg.cconv.bcb);
  320. }
  321. }
  322. struct omap_overlay_manager *find_dss_mgr(int display_ix)
  323. {
  324. struct omap_overlay_manager *mgr;
  325. char name[32];
  326. int i;
  327. sprintf(name, "display%d", display_ix);
  328. for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
  329. mgr = omap_dss_get_overlay_manager(i);
  330. if (mgr->device && !strcmp(name, dev_name(&mgr->device->dev)))
  331. return mgr;
  332. }
  333. return NULL;
  334. }
  335. int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb)
  336. {
  337. struct omap_overlay_manager_info info;
  338. struct omap_overlay_manager *mgr;
  339. if (!mi)
  340. return -EINVAL;
  341. mgr = find_dss_mgr(mi->ix);
  342. if (!mgr)
  343. return -EINVAL;
  344. /* just in case there are new fields, we get the current info */
  345. mgr->get_manager_info(mgr, &info);
  346. info.alpha_enabled = mi->alpha_blending;
  347. info.default_color = mi->default_color;
  348. info.trans_enabled = mi->trans_enabled && !mi->alpha_blending;
  349. info.trans_key = mi->trans_key;
  350. info.trans_key_type = mi->trans_key_type;
  351. info.cpr_coefs = mi->cpr_coefs;
  352. info.cpr_enable = mi->cpr_enabled;
  353. info.cb = *cb;
  354. return mgr->set_manager_info(mgr, &info);
  355. }
  356. void swap_rb_in_mgr_info(struct dss2_mgr_info *mi)
  357. {
  358. const struct omap_dss_cpr_coefs c = { 256, 0, 0, 0, 256, 0, 0, 0, 256 };
  359. /* set default CPR */
  360. if (!mi->cpr_enabled)
  361. mi->cpr_coefs = c;
  362. mi->cpr_enabled = true;
  363. /* swap red and blue */
  364. swap(mi->cpr_coefs.rr, mi->cpr_coefs.br);
  365. swap(mi->cpr_coefs.rg, mi->cpr_coefs.bg);
  366. swap(mi->cpr_coefs.rb, mi->cpr_coefs.bb);
  367. }
  368. /*
  369. * ===========================================================================
  370. * DEBUG METHODS
  371. * ===========================================================================
  372. */
  373. void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi)
  374. {
  375. struct dss2_ovl_cfg *c = &oi->cfg;
  376. const struct color_info *ci;
  377. if (!(debug & DEBUG_OVERLAYS) ||
  378. !(debug & DEBUG_COMPOSITIONS))
  379. return;
  380. ci = get_color_info(c->color_mode);
  381. if (c->zonly) {
  382. dev_info(DEV(cdev), "ovl%d(%s z%d)\n",
  383. c->ix, c->enabled ? "ON" : "off", c->zorder);
  384. return;
  385. }
  386. dev_info(DEV(cdev), "ovl%d(%s z%d %s%s *%d%% %d*%d:%d,%d+%d,%d rot%d%s"
  387. " => %d,%d+%d,%d %p/%p|%d)\n",
  388. c->ix, c->enabled ? "ON" : "off", c->zorder,
  389. ci->name ? : "(none)",
  390. c->pre_mult_alpha ? " premult" : "",
  391. (c->global_alpha * 100 + 128) / 255,
  392. c->width, c->height, c->crop.x, c->crop.y,
  393. c->crop.w, c->crop.h,
  394. c->rotation, c->mirror ? "+mir" : "",
  395. c->win.x, c->win.y, c->win.w, c->win.h,
  396. (void *) oi->ba, (void *) oi->uv, c->stride);
  397. }
  398. static void print_mgr_info(struct dsscomp_dev *cdev,
  399. struct dss2_mgr_info *mi)
  400. {
  401. printk("(dis%d(%s) alpha=%d col=%08x ilace=%d) ",
  402. mi->ix,
  403. (mi->ix < cdev->num_displays && cdev->displays[mi->ix]) ?
  404. cdev->displays[mi->ix]->name : "NONE",
  405. mi->alpha_blending, mi->default_color,
  406. mi->interlaced);
  407. }
  408. void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
  409. const char *phase)
  410. {
  411. if (!(debug & DEBUG_COMPOSITIONS))
  412. return;
  413. dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
  414. *phase == 'q' ? (void *) d->sync_id : d, phase,
  415. (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
  416. (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
  417. (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
  418. print_mgr_info(cdev, &d->mgr);
  419. printk("n=%d\n", d->num_ovls);
  420. }
  421. void dump_total_comp_info(struct dsscomp_dev *cdev,
  422. struct dsscomp_setup_dispc_data *d,
  423. const char *phase)
  424. {
  425. int i;
  426. if (!(debug & DEBUG_COMPOSITIONS))
  427. return;
  428. dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
  429. *phase == 'q' ? (void *) d->sync_id : d, phase,
  430. (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
  431. (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
  432. (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
  433. for (i = 0; i < d->num_mgrs && i < ARRAY_SIZE(d->mgrs); i++)
  434. print_mgr_info(cdev, d->mgrs + i);
  435. printk("n=%d\n", d->num_ovls);
  436. }