mdss_qpic.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/kernel.h>
  14. #include <linux/sched.h>
  15. #include <linux/time.h>
  16. #include <linux/init.h>
  17. #include <linux/interrupt.h>
  18. #include <linux/spinlock.h>
  19. #include <linux/hrtimer.h>
  20. #include <linux/clk.h>
  21. #include <linux/io.h>
  22. #include <linux/debugfs.h>
  23. #include <linux/delay.h>
  24. #include <linux/mutex.h>
  25. #include <linux/regulator/consumer.h>
  26. #include <linux/semaphore.h>
  27. #include <linux/uaccess.h>
  28. #include <linux/bootmem.h>
  29. #include <linux/dma-mapping.h>
  30. #include <asm/system.h>
  31. #include <asm/mach-types.h>
  32. #include <mach/sps.h>
  33. #include <mach/clk.h>
  34. #include <mach/hardware.h>
  35. #include "mdss_fb.h"
  36. #include "mdss_qpic.h"
  37. static int mdss_qpic_probe(struct platform_device *pdev);
  38. static int mdss_qpic_remove(struct platform_device *pdev);
  39. struct qpic_data_type *qpic_res;
  40. /* for tuning */
  41. static u32 use_bam = true;
  42. static u32 use_irq;
  43. static u32 use_vsync;
  44. static const struct of_device_id mdss_qpic_dt_match[] = {
  45. { .compatible = "qcom,mdss_qpic",},
  46. {}
  47. };
  48. MODULE_DEVICE_TABLE(of, mdss_qpic_dt_match);
  49. static struct platform_driver mdss_qpic_driver = {
  50. .probe = mdss_qpic_probe,
  51. .remove = mdss_qpic_remove,
  52. .shutdown = NULL,
  53. .driver = {
  54. /*
  55. * Simulate mdp hw
  56. */
  57. .name = "mdp",
  58. .of_match_table = mdss_qpic_dt_match,
  59. },
  60. };
  61. int qpic_on(struct msm_fb_data_type *mfd)
  62. {
  63. int ret;
  64. ret = mdss_qpic_panel_on(qpic_res->panel_data);
  65. return ret;
  66. }
  67. int qpic_off(struct msm_fb_data_type *mfd)
  68. {
  69. int ret;
  70. ret = mdss_qpic_panel_off(qpic_res->panel_data);
  71. return ret;
  72. }
  73. static void mdss_qpic_pan_display(struct msm_fb_data_type *mfd)
  74. {
  75. struct fb_info *fbi;
  76. u32 offset, fb_offset, size;
  77. int bpp;
  78. if (!mfd) {
  79. pr_err("%s: mfd is NULL!", __func__);
  80. return;
  81. }
  82. fbi = mfd->fbi;
  83. bpp = fbi->var.bits_per_pixel / 8;
  84. offset = fbi->var.xoffset * bpp +
  85. fbi->var.yoffset * fbi->fix.line_length;
  86. if (offset > fbi->fix.smem_len) {
  87. pr_err("invalid fb offset=%u total length=%u\n",
  88. offset, fbi->fix.smem_len);
  89. return;
  90. }
  91. fb_offset = (u32)fbi->fix.smem_start + offset;
  92. mdss_qpic_panel_on(qpic_res->panel_data);
  93. size = fbi->var.xres * fbi->var.yres * bpp;
  94. qpic_send_frame(0, 0, fbi->var.xres, fbi->var.yres,
  95. (u32 *)fb_offset, size);
  96. }
  97. int mdss_qpic_alloc_fb_mem(struct msm_fb_data_type *mfd)
  98. {
  99. size_t size;
  100. u32 yres = mfd->fbi->var.yres_virtual;
  101. size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
  102. if (!qpic_res->res_init)
  103. return -EINVAL;
  104. if (mfd->index != 0) {
  105. mfd->fbi->fix.smem_start = 0;
  106. mfd->fbi->screen_base = NULL;
  107. mfd->fbi->fix.smem_len = 0;
  108. mfd->iova = 0;
  109. return 0;
  110. }
  111. if (!qpic_res->fb_virt) {
  112. qpic_res->fb_virt = (void *)dmam_alloc_coherent(
  113. &qpic_res->pdev->dev,
  114. size + QPIC_MAX_CMD_BUF_SIZE,
  115. &qpic_res->fb_phys,
  116. GFP_KERNEL);
  117. pr_err("%s size=%d vir_addr=%x phys_addr=%x",
  118. __func__, size, (int)qpic_res->fb_virt,
  119. (int)qpic_res->fb_phys);
  120. if (!qpic_res->fb_virt)
  121. return -ENOMEM;
  122. qpic_res->cmd_buf_virt = qpic_res->fb_virt + size;
  123. qpic_res->cmd_buf_phys = qpic_res->fb_phys + size;
  124. }
  125. mfd->fbi->fix.smem_start = qpic_res->fb_phys;
  126. mfd->fbi->screen_base = qpic_res->fb_virt;
  127. mfd->fbi->fix.smem_len = size;
  128. mfd->iova = 0;
  129. return 0;
  130. }
  131. u32 mdss_qpic_fb_stride(u32 fb_index, u32 xres, int bpp)
  132. {
  133. return xres * bpp;
  134. }
  135. int mdss_qpic_overlay_init(struct msm_fb_data_type *mfd)
  136. {
  137. struct msm_mdp_interface *qpic_interface = &mfd->mdp;
  138. qpic_interface->on_fnc = qpic_on;
  139. qpic_interface->off_fnc = qpic_off;
  140. qpic_interface->do_histogram = NULL;
  141. qpic_interface->cursor_update = NULL;
  142. qpic_interface->dma_fnc = mdss_qpic_pan_display;
  143. qpic_interface->ioctl_handler = NULL;
  144. qpic_interface->kickoff_fnc = NULL;
  145. return 0;
  146. }
  147. int qpic_register_panel(struct mdss_panel_data *pdata)
  148. {
  149. struct platform_device *mdss_fb_dev = NULL;
  150. int rc;
  151. mdss_fb_dev = platform_device_alloc("mdss_fb", pdata->panel_info.pdest);
  152. if (!mdss_fb_dev) {
  153. pr_err("unable to allocate mdss_fb device\n");
  154. return -ENOMEM;
  155. }
  156. mdss_fb_dev->dev.platform_data = pdata;
  157. rc = platform_device_add(mdss_fb_dev);
  158. if (rc) {
  159. platform_device_put(mdss_fb_dev);
  160. pr_err("unable to probe mdss_fb device (%d)\n", rc);
  161. return rc;
  162. }
  163. qpic_res->panel_data = pdata;
  164. return rc;
  165. }
  166. int qpic_init_sps(struct platform_device *pdev,
  167. struct qpic_sps_endpt *end_point)
  168. {
  169. int rc = 0;
  170. struct sps_pipe *pipe_handle;
  171. struct sps_connect *sps_config = &end_point->config;
  172. struct sps_register_event *sps_event = &end_point->bam_event;
  173. struct sps_bam_props bam = {0};
  174. u32 bam_handle = 0;
  175. if (qpic_res->sps_init)
  176. return 0;
  177. bam.phys_addr = qpic_res->qpic_phys + 0x4000;
  178. bam.virt_addr = qpic_res->qpic_base + 0x4000;
  179. bam.irq = qpic_res->irq - 4;
  180. bam.manage = SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_MULTI_EE;
  181. rc = sps_phy2h(bam.phys_addr, &bam_handle);
  182. if (rc)
  183. rc = sps_register_bam_device(&bam, &bam_handle);
  184. if (rc) {
  185. pr_err("%s bam_handle is NULL", __func__);
  186. rc = -ENOMEM;
  187. goto out;
  188. }
  189. pipe_handle = sps_alloc_endpoint();
  190. if (!pipe_handle) {
  191. pr_err("sps_alloc_endpoint() failed\n");
  192. rc = -ENOMEM;
  193. goto out;
  194. }
  195. rc = sps_get_config(pipe_handle, sps_config);
  196. if (rc) {
  197. pr_err("sps_get_config() failed %d\n", rc);
  198. goto free_endpoint;
  199. }
  200. /* WRITE CASE: source - system memory; destination - BAM */
  201. sps_config->source = SPS_DEV_HANDLE_MEM;
  202. sps_config->destination = bam_handle;
  203. sps_config->mode = SPS_MODE_DEST;
  204. sps_config->dest_pipe_index = 6;
  205. sps_config->options = SPS_O_AUTO_ENABLE | SPS_O_EOT;
  206. sps_config->lock_group = 0;
  207. /*
  208. * Descriptor FIFO is a cyclic FIFO. If 64 descriptors
  209. * are allowed to be submitted before we get any ack for any of them,
  210. * the descriptor FIFO size should be: (SPS_MAX_DESC_NUM + 1) *
  211. * sizeof(struct sps_iovec).
  212. */
  213. sps_config->desc.size = (64) *
  214. sizeof(struct sps_iovec);
  215. sps_config->desc.base = dmam_alloc_coherent(&pdev->dev,
  216. sps_config->desc.size,
  217. &sps_config->desc.phys_base,
  218. GFP_KERNEL);
  219. if (!sps_config->desc.base) {
  220. pr_err("dmam_alloc_coherent() failed for size %x\n",
  221. sps_config->desc.size);
  222. rc = -ENOMEM;
  223. goto free_endpoint;
  224. }
  225. memset(sps_config->desc.base, 0x00, sps_config->desc.size);
  226. rc = sps_connect(pipe_handle, sps_config);
  227. if (rc) {
  228. pr_err("sps_connect() failed %d\n", rc);
  229. goto free_endpoint;
  230. }
  231. init_completion(&end_point->completion);
  232. sps_event->mode = SPS_TRIGGER_WAIT;
  233. sps_event->options = SPS_O_EOT;
  234. sps_event->xfer_done = &end_point->completion;
  235. sps_event->user = (void *)qpic_res;
  236. rc = sps_register_event(pipe_handle, sps_event);
  237. if (rc) {
  238. pr_err("sps_register_event() failed %d\n", rc);
  239. goto sps_disconnect;
  240. }
  241. end_point->handle = pipe_handle;
  242. qpic_res->sps_init = true;
  243. goto out;
  244. sps_disconnect:
  245. sps_disconnect(pipe_handle);
  246. free_endpoint:
  247. sps_free_endpoint(pipe_handle);
  248. out:
  249. return rc;
  250. }
  251. void mdss_qpic_reset(void)
  252. {
  253. u32 time_end;
  254. QPIC_OUTP(QPIC_REG_QPIC_LCDC_RESET, 1 << 0);
  255. /* wait 100 us after reset as suggested by hw */
  256. usleep(100);
  257. time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
  258. while (((QPIC_INP(QPIC_REG_QPIC_LCDC_STTS) & (1 << 8)) == 0)) {
  259. if ((u32)ktime_to_ms(ktime_get()) > time_end) {
  260. pr_err("%s reset not finished", __func__);
  261. break;
  262. }
  263. /* yield 100 us for next polling by experiment*/
  264. usleep(100);
  265. }
  266. }
  267. void qpic_interrupt_en(u32 en)
  268. {
  269. QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
  270. if (en) {
  271. if (!qpic_res->irq_ena) {
  272. qpic_res->irq_ena = true;
  273. enable_irq(qpic_res->irq);
  274. QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN,
  275. (1 << 0) | (1 << 2));
  276. }
  277. } else {
  278. QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);
  279. disable_irq(qpic_res->irq);
  280. qpic_res->irq_ena = false;
  281. }
  282. }
  283. static irqreturn_t qpic_irq_handler(int irq, void *ptr)
  284. {
  285. u32 data;
  286. data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
  287. QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
  288. return 0;
  289. }
  290. int qpic_flush_buffer_bam(u32 cmd, u32 len, u32 *param, u32 is_cmd)
  291. {
  292. int ret = 0;
  293. u32 phys_addr, cfg2, block_len , flags;
  294. if (is_cmd) {
  295. memcpy((u8 *)qpic_res->cmd_buf_virt, param, len);
  296. invalidate_caches((unsigned long)qpic_res->cmd_buf_virt,
  297. len,
  298. (unsigned long)qpic_res->cmd_buf_phys);
  299. phys_addr = qpic_res->cmd_buf_phys;
  300. } else {
  301. phys_addr = (u32)param;
  302. }
  303. cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
  304. cfg2 &= ~0xFF;
  305. cfg2 |= cmd;
  306. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
  307. block_len = 0x7FF0;
  308. while (len > 0) {
  309. if (len <= 0x7FF0) {
  310. flags = SPS_IOVEC_FLAG_EOT;
  311. block_len = len;
  312. } else {
  313. flags = 0;
  314. }
  315. ret = sps_transfer_one(qpic_res->qpic_endpt.handle,
  316. phys_addr, block_len, NULL, flags);
  317. if (ret)
  318. pr_err("failed to submit command %x ret %d\n",
  319. cmd, ret);
  320. phys_addr += block_len;
  321. len -= block_len;
  322. }
  323. ret = wait_for_completion_timeout(
  324. &qpic_res->qpic_endpt.completion,
  325. msecs_to_jiffies(100 * 4));
  326. if (ret <= 0)
  327. pr_err("%s timeout %x", __func__, ret);
  328. else
  329. ret = 0;
  330. return ret;
  331. }
  332. int qpic_flush_buffer_sw(u32 cmd, u32 len, u32 *param, u32 is_cmd)
  333. {
  334. u32 bytes_left, space, data, cfg2, time_end;
  335. int i, ret = 0;
  336. if ((len <= (sizeof(u32) * 4)) && (is_cmd)) {
  337. len >>= 2;/* len in dwords */
  338. data = 0;
  339. for (i = 0; i < len; i++)
  340. data |= param[i] << (8 * i);
  341. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, len);
  342. QPIC_OUTP(QPIC_REG_LCD_DEVICE_CMD0 + (4 * cmd), data);
  343. return 0;
  344. }
  345. if ((len & 0x1) != 0) {
  346. pr_err("%s: number of bytes needs be even", __func__);
  347. len = (len + 1) & (~0x1);
  348. }
  349. QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
  350. cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
  351. cfg2 |= (1 << 24); /* transparent mode */
  352. cfg2 &= ~0xFF;
  353. cfg2 |= cmd;
  354. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
  355. QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_SOF, 0x0);
  356. bytes_left = len;
  357. while (bytes_left > 0) {
  358. time_end = (u32)ktime_to_ms(ktime_get()) +
  359. QPIC_MAX_VSYNC_WAIT_TIME;
  360. while (1) {
  361. data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS);
  362. data &= 0x3F;
  363. if (data == 0)
  364. break;
  365. /* yield 10 us for next polling by experiment*/
  366. usleep(10);
  367. if (ktime_to_ms(ktime_get()) > time_end) {
  368. pr_err("%s time out", __func__);
  369. ret = -EBUSY;
  370. goto exit_send_cmd_sw;
  371. }
  372. }
  373. space = (16 - data);
  374. while ((space > 0) && (bytes_left > 0)) {
  375. /* write to fifo */
  376. if (bytes_left >= 4) {
  377. QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0,
  378. param[0]);
  379. param++;
  380. bytes_left -= 4;
  381. space--;
  382. } else if (bytes_left == 2) {
  383. QPIC_OUTPW(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0,
  384. *(u16 *)param);
  385. bytes_left -= 2;
  386. }
  387. }
  388. }
  389. /* finished */
  390. QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_EOF, 0x0);
  391. time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
  392. while (1) {
  393. data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
  394. if (data & (1 << 2))
  395. break;
  396. /* yield 10 us for next polling by experiment*/
  397. usleep(10);
  398. if (ktime_to_ms(ktime_get()) > time_end) {
  399. pr_err("%s wait for eof time out", __func__);
  400. ret = -EBUSY;
  401. goto exit_send_cmd_sw;
  402. }
  403. }
  404. exit_send_cmd_sw:
  405. cfg2 &= ~(1 << 24);
  406. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
  407. return ret;
  408. }
  409. int qpic_flush_buffer(u32 cmd, u32 len, u32 *param, u32 is_cmd)
  410. {
  411. if (use_bam) {
  412. if (is_cmd)
  413. return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
  414. else
  415. return qpic_flush_buffer_bam(cmd, len, param, is_cmd);
  416. } else {
  417. return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
  418. }
  419. }
  420. int mdss_qpic_init(void)
  421. {
  422. int ret = 0;
  423. u32 data;
  424. mdss_qpic_reset();
  425. pr_info("%s version=%x", __func__, QPIC_INP(QPIC_REG_LCDC_VERSION));
  426. data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
  427. /* clear vsync wait , bam mode = 0*/
  428. data &= ~(3 << 0);
  429. data &= ~(0x1f << 3);
  430. data |= (1 << 3); /* threshold */
  431. data |= (1 << 8); /* lcd_en */
  432. data &= ~(0x1f << 9);
  433. data |= (1 << 9); /* threshold */
  434. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
  435. if (use_irq && qpic_res->irq_requested) {
  436. ret = devm_request_irq(&qpic_res->pdev->dev,
  437. qpic_res->irq, qpic_irq_handler,
  438. IRQF_DISABLED, "QPIC", qpic_res);
  439. if (ret) {
  440. pr_err("qpic request_irq() failed!\n");
  441. use_irq = false;
  442. }
  443. qpic_res->irq_requested = true;
  444. }
  445. qpic_interrupt_en(use_irq);
  446. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG0, 0x02108501);
  447. data = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
  448. data &= ~(0xFFF);
  449. data |= 0x200; /* XRGB */
  450. data |= 0x2C;
  451. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, data);
  452. if (use_bam) {
  453. qpic_init_sps(qpic_res->pdev , &qpic_res->qpic_endpt);
  454. data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
  455. data |= (1 << 1);
  456. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
  457. }
  458. /* TE enable */
  459. if (use_vsync) {
  460. data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
  461. data |= (1 << 0);
  462. QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
  463. }
  464. return ret;
  465. }
  466. static int mdss_qpic_probe(struct platform_device *pdev)
  467. {
  468. struct resource *res;
  469. int rc = 0;
  470. static struct msm_mdp_interface qpic_interface = {
  471. .init_fnc = mdss_qpic_overlay_init,
  472. .fb_mem_alloc_fnc = mdss_qpic_alloc_fb_mem,
  473. .fb_stride = mdss_qpic_fb_stride,
  474. };
  475. if (!pdev->dev.of_node) {
  476. pr_err("qpic driver only supports device tree probe\n");
  477. return -ENOTSUPP;
  478. }
  479. if (!qpic_res)
  480. qpic_res = devm_kzalloc(&pdev->dev,
  481. sizeof(*qpic_res), GFP_KERNEL);
  482. if (!qpic_res)
  483. return -ENOMEM;
  484. if (qpic_res->res_init) {
  485. pr_err("qpic already initialized\n");
  486. return -EINVAL;
  487. }
  488. pdev->id = 0;
  489. qpic_res->pdev = pdev;
  490. platform_set_drvdata(pdev, qpic_res);
  491. res = platform_get_resource_byname(pdev,
  492. IORESOURCE_MEM, "qpic_base");
  493. if (!res) {
  494. pr_err("unable to get QPIC reg base address\n");
  495. rc = -ENOMEM;
  496. goto probe_done;
  497. }
  498. qpic_res->qpic_reg_size = resource_size(res);
  499. qpic_res->qpic_base = devm_ioremap(&pdev->dev, res->start,
  500. qpic_res->qpic_reg_size);
  501. if (unlikely(!qpic_res->qpic_base)) {
  502. pr_err("unable to map MDSS QPIC base\n");
  503. rc = -ENOMEM;
  504. goto probe_done;
  505. }
  506. qpic_res->qpic_phys = res->start;
  507. pr_info("MDSS QPIC HW Base phy_Address=0x%x virt=0x%x\n",
  508. (int) res->start,
  509. (int) qpic_res->qpic_base);
  510. res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  511. if (!res) {
  512. pr_err("unable to get QPIC irq\n");
  513. rc = -ENOMEM;
  514. goto probe_done;
  515. }
  516. qpic_res->irq = res->start;
  517. qpic_res->res_init = true;
  518. rc = mdss_fb_register_mdp_instance(&qpic_interface);
  519. if (rc)
  520. pr_err("unable to register QPIC instance\n");
  521. probe_done:
  522. return rc;
  523. }
  524. static int mdss_qpic_remove(struct platform_device *pdev)
  525. {
  526. return 0;
  527. }
  528. static int __init mdss_qpic_driver_init(void)
  529. {
  530. int ret;
  531. ret = platform_driver_register(&mdss_qpic_driver);
  532. if (ret)
  533. pr_err("mdss_qpic_register_driver() failed!\n");
  534. return ret;
  535. }
  536. module_init(mdss_qpic_driver_init);