PageRenderTime 69ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/video/msm/mdp_lcdc.c

https://bitbucket.org/androidarmv6/android_kernel_samsung_msm7x27
C | 432 lines | 324 code | 81 blank | 27 comment | 16 complexity | 35993be699c566905bc0b8fa5c0f77ae MD5 | raw file
  1. /* drivers/video/msm/mdp_lcdc.c
  2. *
  3. * Copyright (c) 2009 Google Inc.
  4. * Copyright (c) 2009 QUALCOMM Incorporated
  5. *
  6. * This software is licensed under the terms of the GNU General Public
  7. * License version 2, as published by the Free Software Foundation, and
  8. * may be copied, distributed, and modified under those terms.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * Author: Dima Zavin <dima@android.com>
  16. */
  17. #include <linux/clk.h>
  18. #include <linux/err.h>
  19. #include <linux/init.h>
  20. #include <linux/kernel.h>
  21. #include <linux/platform_device.h>
  22. #include <linux/sched.h>
  23. #include <linux/wait.h>
  24. #include <linux/delay.h>
  25. #include <asm/io.h>
  26. #include <asm/mach-types.h>
  27. #include <mach/msm_fb.h>
  28. #include "mdp_hw.h"
  29. struct mdp_lcdc_info {
  30. struct mdp_info *mdp;
  31. struct clk *mdp_clk;
  32. struct clk *pclk;
  33. struct clk *pad_pclk;
  34. struct msm_panel_data fb_panel_data;
  35. struct platform_device fb_pdev;
  36. struct msm_lcdc_platform_data *pdata;
  37. uint32_t fb_start;
  38. struct msmfb_callback frame_start_cb;
  39. wait_queue_head_t vsync_waitq;
  40. int got_vsync;
  41. struct {
  42. uint32_t clk_rate;
  43. uint32_t hsync_ctl;
  44. uint32_t vsync_period;
  45. uint32_t vsync_pulse_width;
  46. uint32_t display_hctl;
  47. uint32_t display_vstart;
  48. uint32_t display_vend;
  49. uint32_t hsync_skew;
  50. uint32_t polarity;
  51. } parms;
  52. };
  53. static struct mdp_device *mdp_dev;
  54. #define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
  55. static int lcdc_unblank(struct msm_panel_data *fb_panel)
  56. {
  57. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  58. struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
  59. pr_info("%s: ()\n", __func__);
  60. panel_ops->unblank(panel_ops);
  61. return 0;
  62. }
  63. static int lcdc_blank(struct msm_panel_data *fb_panel)
  64. {
  65. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  66. struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
  67. pr_info("%s: ()\n", __func__);
  68. panel_ops->blank(panel_ops);
  69. return 0;
  70. }
  71. static int lcdc_suspend(struct msm_panel_data *fb_panel)
  72. {
  73. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  74. pr_info("%s: suspending\n", __func__);
  75. mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
  76. clk_disable(lcdc->pad_pclk);
  77. clk_disable(lcdc->pclk);
  78. clk_disable(lcdc->mdp_clk);
  79. return 0;
  80. }
  81. static int lcdc_resume(struct msm_panel_data *fb_panel)
  82. {
  83. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  84. pr_info("%s: resuming\n", __func__);
  85. clk_enable(lcdc->mdp_clk);
  86. clk_enable(lcdc->pclk);
  87. clk_enable(lcdc->pad_pclk);
  88. mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
  89. return 0;
  90. }
  91. static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
  92. {
  93. struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
  94. uint32_t dma_cfg;
  95. clk_enable(lcdc->mdp_clk);
  96. clk_enable(lcdc->pclk);
  97. clk_enable(lcdc->pad_pclk);
  98. clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
  99. clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
  100. /* write the lcdc params */
  101. mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
  102. mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
  103. mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
  104. MDP_LCDC_VSYNC_PULSE_WIDTH);
  105. mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
  106. mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
  107. MDP_LCDC_DISPLAY_V_START);
  108. mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
  109. mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
  110. mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
  111. mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
  112. mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
  113. mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
  114. mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
  115. mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
  116. /* config the dma_p block that drives the lcdc data */
  117. mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
  118. mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
  119. (fb_panel->fb_data->xres & 0x7ff)),
  120. MDP_DMA_P_SIZE);
  121. mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
  122. dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
  123. dma_cfg |= (DMA_PACK_ALIGN_LSB |
  124. DMA_PACK_PATTERN_RGB |
  125. DMA_DITHER_EN);
  126. dma_cfg |= DMA_OUT_SEL_LCDC;
  127. dma_cfg &= ~DMA_DST_BITS_MASK;
  128. if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
  129. dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
  130. else
  131. dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
  132. mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
  133. /* enable the lcdc timing generation */
  134. mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
  135. return 0;
  136. }
  137. static void lcdc_wait_vsync(struct msm_panel_data *panel)
  138. {
  139. struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
  140. int ret;
  141. ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
  142. if (!ret && !lcdc->got_vsync)
  143. pr_err("%s: timeout waiting for VSYNC\n", __func__);
  144. lcdc->got_vsync = 0;
  145. }
  146. static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
  147. struct msmfb_callback *vsync_cb)
  148. {
  149. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  150. /* the vsync callback will start the dma */
  151. vsync_cb->func(vsync_cb);
  152. lcdc->got_vsync = 0;
  153. mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
  154. &lcdc->frame_start_cb);
  155. lcdc_wait_vsync(fb_panel);
  156. }
  157. static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
  158. {
  159. struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
  160. lcdc->got_vsync = 0;
  161. mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
  162. }
  163. /* called in irq context with mdp lock held, when mdp gets the
  164. * MDP_LCDC_FRAME_START interrupt */
  165. static void lcdc_frame_start(struct msmfb_callback *cb)
  166. {
  167. struct mdp_lcdc_info *lcdc;
  168. lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
  169. lcdc->got_vsync = 1;
  170. wake_up(&lcdc->vsync_waitq);
  171. }
  172. static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
  173. uint32_t width, uint32_t height, uint32_t x,
  174. uint32_t y)
  175. {
  176. struct mdp_lcdc_info *lcdc = priv;
  177. struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
  178. if (mdp->dma_config_dirty)
  179. {
  180. mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
  181. mdelay(20);
  182. mdp_dev->configure_dma(mdp_dev);
  183. mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
  184. }
  185. mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
  186. mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
  187. }
  188. static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
  189. {
  190. struct msm_lcdc_timing *timing = lcdc->pdata->timing;
  191. struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
  192. unsigned int hsync_period;
  193. unsigned int hsync_start_x;
  194. unsigned int hsync_end_x;
  195. unsigned int vsync_period;
  196. unsigned int display_vstart;
  197. unsigned int display_vend;
  198. hsync_period = (timing->hsync_back_porch +
  199. fb_data->xres + timing->hsync_front_porch);
  200. hsync_start_x = timing->hsync_back_porch;
  201. hsync_end_x = hsync_start_x + fb_data->xres - 1;
  202. vsync_period = (timing->vsync_back_porch +
  203. fb_data->yres + timing->vsync_front_porch);
  204. vsync_period *= hsync_period;
  205. display_vstart = timing->vsync_back_porch;
  206. display_vstart *= hsync_period;
  207. display_vstart += timing->hsync_skew;
  208. display_vend = (timing->vsync_back_porch + fb_data->yres) *
  209. hsync_period;
  210. display_vend += timing->hsync_skew - 1;
  211. /* register values we pre-compute at init time from the timing
  212. * information in the panel info */
  213. lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
  214. (timing->hsync_pulse_width & 0xfff));
  215. lcdc->parms.vsync_period = vsync_period & 0xffffff;
  216. lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
  217. hsync_period) & 0xffffff;
  218. lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
  219. (hsync_start_x & 0xfff));
  220. lcdc->parms.display_vstart = display_vstart & 0xffffff;
  221. lcdc->parms.display_vend = display_vend & 0xffffff;
  222. lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
  223. lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
  224. (timing->vsync_act_low << 1) |
  225. (timing->den_act_low << 2));
  226. lcdc->parms.clk_rate = timing->clk_rate;
  227. }
  228. static int mdp_lcdc_probe(struct platform_device *pdev)
  229. {
  230. struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
  231. struct mdp_lcdc_info *lcdc;
  232. int ret = 0;
  233. if (!pdata) {
  234. pr_err("%s: no LCDC platform data found\n", __func__);
  235. return -EINVAL;
  236. }
  237. lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
  238. if (!lcdc)
  239. return -ENOMEM;
  240. /* We don't actually own the clocks, the mdp does. */
  241. lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
  242. if (IS_ERR(lcdc->mdp_clk)) {
  243. pr_err("%s: failed to get mdp_clk\n", __func__);
  244. ret = PTR_ERR(lcdc->mdp_clk);
  245. goto err_get_mdp_clk;
  246. }
  247. lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
  248. if (IS_ERR(lcdc->pclk)) {
  249. pr_err("%s: failed to get lcdc_pclk\n", __func__);
  250. ret = PTR_ERR(lcdc->pclk);
  251. goto err_get_pclk;
  252. }
  253. lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
  254. if (IS_ERR(lcdc->pad_pclk)) {
  255. pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
  256. ret = PTR_ERR(lcdc->pad_pclk);
  257. goto err_get_pad_pclk;
  258. }
  259. init_waitqueue_head(&lcdc->vsync_waitq);
  260. lcdc->pdata = pdata;
  261. lcdc->frame_start_cb.func = lcdc_frame_start;
  262. platform_set_drvdata(pdev, lcdc);
  263. mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
  264. lcdc_dma_start);
  265. precompute_timing_parms(lcdc);
  266. lcdc->fb_start = pdata->fb_resource->start;
  267. lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
  268. lcdc->fb_panel_data.suspend = lcdc_suspend;
  269. lcdc->fb_panel_data.resume = lcdc_resume;
  270. lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
  271. lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
  272. lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
  273. lcdc->fb_panel_data.blank = lcdc_blank;
  274. lcdc->fb_panel_data.unblank = lcdc_unblank;
  275. lcdc->fb_panel_data.fb_data = pdata->fb_data;
  276. lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
  277. ret = lcdc_hw_init(lcdc);
  278. if (ret) {
  279. pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
  280. goto err_hw_init;
  281. }
  282. lcdc->fb_pdev.name = "msm_panel";
  283. lcdc->fb_pdev.id = pdata->fb_id;
  284. lcdc->fb_pdev.resource = pdata->fb_resource;
  285. lcdc->fb_pdev.num_resources = 1;
  286. lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
  287. if (pdata->panel_ops->init)
  288. pdata->panel_ops->init(pdata->panel_ops);
  289. ret = platform_device_register(&lcdc->fb_pdev);
  290. if (ret) {
  291. pr_err("%s: Cannot register msm_panel pdev\n", __func__);
  292. goto err_plat_dev_reg;
  293. }
  294. pr_info("%s: initialized\n", __func__);
  295. return 0;
  296. err_plat_dev_reg:
  297. err_hw_init:
  298. platform_set_drvdata(pdev, NULL);
  299. clk_put(lcdc->pad_pclk);
  300. err_get_pad_pclk:
  301. clk_put(lcdc->pclk);
  302. err_get_pclk:
  303. clk_put(lcdc->mdp_clk);
  304. err_get_mdp_clk:
  305. kfree(lcdc);
  306. return ret;
  307. }
  308. static int mdp_lcdc_remove(struct platform_device *pdev)
  309. {
  310. struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
  311. platform_set_drvdata(pdev, NULL);
  312. clk_put(lcdc->pclk);
  313. clk_put(lcdc->pad_pclk);
  314. kfree(lcdc);
  315. return 0;
  316. }
  317. static struct platform_driver mdp_lcdc_driver = {
  318. .probe = mdp_lcdc_probe,
  319. .remove = mdp_lcdc_remove,
  320. .driver = {
  321. .name = "msm_mdp_lcdc",
  322. .owner = THIS_MODULE,
  323. },
  324. };
  325. static int mdp_lcdc_add_mdp_device(struct device *dev,
  326. struct class_interface *class_intf)
  327. {
  328. /* might need locking if mulitple mdp devices */
  329. if (mdp_dev)
  330. return 0;
  331. mdp_dev = container_of(dev, struct mdp_device, dev);
  332. return platform_driver_register(&mdp_lcdc_driver);
  333. }
  334. static void mdp_lcdc_remove_mdp_device(struct device *dev,
  335. struct class_interface *class_intf)
  336. {
  337. /* might need locking if mulitple mdp devices */
  338. if (dev != &mdp_dev->dev)
  339. return;
  340. platform_driver_unregister(&mdp_lcdc_driver);
  341. mdp_dev = NULL;
  342. }
  343. static struct class_interface mdp_lcdc_interface = {
  344. .add_dev = &mdp_lcdc_add_mdp_device,
  345. .remove_dev = &mdp_lcdc_remove_mdp_device,
  346. };
  347. static int __init mdp_lcdc_init(void)
  348. {
  349. return register_mdp_client(&mdp_lcdc_interface);
  350. }
  351. module_init(mdp_lcdc_init);