blob: be8d39de962e72edac8a34c267759570751c46b1 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/video/msm/mdp_lcdc.c
2 *
3 * Copyright (c) 2009 Google Inc.
4 * Copyright (c) 2009 Code Aurora Forum. All rights reserved.
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
18#include <linux/clk.h>
19#include <linux/err.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/platform_device.h>
23#include <linux/sched.h>
24#include <linux/wait.h>
25#include <linux/delay.h>
26
27#include <asm/io.h>
28#include <asm/mach-types.h>
29
30#include <mach/msm_fb.h>
31
32#include "mdp_hw.h"
33
34struct mdp_lcdc_info {
35 struct mdp_info *mdp;
36 struct clk *mdp_clk;
37 struct clk *pclk;
38 struct clk *pad_pclk;
39 struct msm_panel_data fb_panel_data;
40 struct platform_device fb_pdev;
41 struct msm_lcdc_platform_data *pdata;
42 uint32_t fb_start;
43
44 struct msmfb_callback frame_start_cb;
45 wait_queue_head_t vsync_waitq;
46 int got_vsync;
47
48 struct {
49 uint32_t clk_rate;
50 uint32_t hsync_ctl;
51 uint32_t vsync_period;
52 uint32_t vsync_pulse_width;
53 uint32_t display_hctl;
54 uint32_t display_vstart;
55 uint32_t display_vend;
56 uint32_t hsync_skew;
57 uint32_t polarity;
58 } parms;
59};
60
61static struct mdp_device *mdp_dev;
62
63#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
64
65static int lcdc_unblank(struct msm_panel_data *fb_panel)
66{
67 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
68 struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
69
70 pr_info("%s: ()\n", __func__);
71 panel_ops->unblank(panel_ops);
72
73 return 0;
74}
75
76static int lcdc_blank(struct msm_panel_data *fb_panel)
77{
78 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
79 struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
80
81 pr_info("%s: ()\n", __func__);
82 panel_ops->blank(panel_ops);
83
84 return 0;
85}
86
87static int lcdc_suspend(struct msm_panel_data *fb_panel)
88{
89 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
90
91 pr_info("%s: suspending\n", __func__);
92
93 mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
94 clk_disable(lcdc->pad_pclk);
95 clk_disable(lcdc->pclk);
96 clk_disable(lcdc->mdp_clk);
97
98 return 0;
99}
100
101static int lcdc_resume(struct msm_panel_data *fb_panel)
102{
103 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
104
105 pr_info("%s: resuming\n", __func__);
106
107 clk_enable(lcdc->mdp_clk);
108 clk_enable(lcdc->pclk);
109 clk_enable(lcdc->pad_pclk);
110 mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
111
112 return 0;
113}
114
115static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
116{
117 struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
118 uint32_t dma_cfg;
119
120 clk_enable(lcdc->mdp_clk);
121 clk_enable(lcdc->pclk);
122 clk_enable(lcdc->pad_pclk);
123
124 clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
125 clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
126
127 /* write the lcdc params */
128 mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
129 mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
130 mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
131 MDP_LCDC_VSYNC_PULSE_WIDTH);
132 mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
133 mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
134 MDP_LCDC_DISPLAY_V_START);
135 mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
136 mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
137
138 mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
139 mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
140 mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
141 mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
142 mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
143 mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
144
145 /* config the dma_p block that drives the lcdc data */
146 mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
147 mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
148 (fb_panel->fb_data->xres & 0x7ff)),
149 MDP_DMA_P_SIZE);
150
151 mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
152
153 dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
154 dma_cfg |= (DMA_PACK_ALIGN_LSB |
155 DMA_PACK_PATTERN_RGB |
156 DMA_DITHER_EN);
157 dma_cfg |= DMA_OUT_SEL_LCDC;
158 dma_cfg &= ~DMA_DST_BITS_MASK;
159
160 if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
161 dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
162 else
163 dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
164
165 mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
166
167 /* enable the lcdc timing generation */
168 mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
169
170 return 0;
171}
172
173static void lcdc_wait_vsync(struct msm_panel_data *panel)
174{
175 struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
176 int ret;
177
178 ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
179 if (!ret && !lcdc->got_vsync)
180 pr_err("%s: timeout waiting for VSYNC\n", __func__);
181 lcdc->got_vsync = 0;
182}
183
184static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
185 struct msmfb_callback *vsync_cb)
186{
187 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
188
189 /* the vsync callback will start the dma */
190 vsync_cb->func(vsync_cb);
191 lcdc->got_vsync = 0;
192 mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
193 &lcdc->frame_start_cb);
194 lcdc_wait_vsync(fb_panel);
195}
196
197static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
198{
199 struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
200 lcdc->got_vsync = 0;
201 mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
202}
203
204/* called in irq context with mdp lock held, when mdp gets the
205 * MDP_LCDC_FRAME_START interrupt */
206static void lcdc_frame_start(struct msmfb_callback *cb)
207{
208 struct mdp_lcdc_info *lcdc;
209
210 lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
211
212 lcdc->got_vsync = 1;
213 wake_up(&lcdc->vsync_waitq);
214}
215
216static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
217 uint32_t width, uint32_t height, uint32_t x,
218 uint32_t y)
219{
220 struct mdp_lcdc_info *lcdc = priv;
221
222 struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
223 if (mdp->dma_config_dirty)
224 {
225 mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
226 mdelay(20);
227 mdp_dev->configure_dma(mdp_dev);
228 mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
229 }
230 mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
231 mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
232}
233
234static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
235{
236 struct msm_lcdc_timing *timing = lcdc->pdata->timing;
237 struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
238 unsigned int hsync_period;
239 unsigned int hsync_start_x;
240 unsigned int hsync_end_x;
241 unsigned int vsync_period;
242 unsigned int display_vstart;
243 unsigned int display_vend;
244
245 hsync_period = (timing->hsync_back_porch +
246 fb_data->xres + timing->hsync_front_porch);
247 hsync_start_x = timing->hsync_back_porch;
248 hsync_end_x = hsync_start_x + fb_data->xres - 1;
249
250 vsync_period = (timing->vsync_back_porch +
251 fb_data->yres + timing->vsync_front_porch);
252 vsync_period *= hsync_period;
253
254 display_vstart = timing->vsync_back_porch;
255 display_vstart *= hsync_period;
256 display_vstart += timing->hsync_skew;
257
258 display_vend = (timing->vsync_back_porch + fb_data->yres) *
259 hsync_period;
260 display_vend += timing->hsync_skew - 1;
261
262 /* register values we pre-compute at init time from the timing
263 * information in the panel info */
264 lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
265 (timing->hsync_pulse_width & 0xfff));
266 lcdc->parms.vsync_period = vsync_period & 0xffffff;
267 lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
268 hsync_period) & 0xffffff;
269
270 lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
271 (hsync_start_x & 0xfff));
272 lcdc->parms.display_vstart = display_vstart & 0xffffff;
273 lcdc->parms.display_vend = display_vend & 0xffffff;
274 lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
275 lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
276 (timing->vsync_act_low << 1) |
277 (timing->den_act_low << 2));
278 lcdc->parms.clk_rate = timing->clk_rate;
279}
280
281static int mdp_lcdc_probe(struct platform_device *pdev)
282{
283 struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
284 struct mdp_lcdc_info *lcdc;
285 int ret = 0;
286
287 if (!pdata) {
288 pr_err("%s: no LCDC platform data found\n", __func__);
289 return -EINVAL;
290 }
291
292 lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
293 if (!lcdc)
294 return -ENOMEM;
295
296 /* We don't actually own the clocks, the mdp does. */
297 lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
298 if (IS_ERR(lcdc->mdp_clk)) {
299 pr_err("%s: failed to get mdp_clk\n", __func__);
300 ret = PTR_ERR(lcdc->mdp_clk);
301 goto err_get_mdp_clk;
302 }
303
304 lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
305 if (IS_ERR(lcdc->pclk)) {
306 pr_err("%s: failed to get lcdc_pclk\n", __func__);
307 ret = PTR_ERR(lcdc->pclk);
308 goto err_get_pclk;
309 }
310
311 lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
312 if (IS_ERR(lcdc->pad_pclk)) {
313 pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
314 ret = PTR_ERR(lcdc->pad_pclk);
315 goto err_get_pad_pclk;
316 }
317
318 init_waitqueue_head(&lcdc->vsync_waitq);
319 lcdc->pdata = pdata;
320 lcdc->frame_start_cb.func = lcdc_frame_start;
321
322 platform_set_drvdata(pdev, lcdc);
323
324 mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
325 lcdc_dma_start);
326
327 precompute_timing_parms(lcdc);
328
329 lcdc->fb_start = pdata->fb_resource->start;
330 lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
331
332 lcdc->fb_panel_data.suspend = lcdc_suspend;
333 lcdc->fb_panel_data.resume = lcdc_resume;
334 lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
335 lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
336 lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
337 lcdc->fb_panel_data.blank = lcdc_blank;
338 lcdc->fb_panel_data.unblank = lcdc_unblank;
339 lcdc->fb_panel_data.fb_data = pdata->fb_data;
340 lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
341
342 ret = lcdc_hw_init(lcdc);
343 if (ret) {
344 pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
345 goto err_hw_init;
346 }
347
348 lcdc->fb_pdev.name = "msm_panel";
349 lcdc->fb_pdev.id = pdata->fb_id;
350 lcdc->fb_pdev.resource = pdata->fb_resource;
351 lcdc->fb_pdev.num_resources = 1;
352 lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
353
354 if (pdata->panel_ops->init)
355 pdata->panel_ops->init(pdata->panel_ops);
356
357 ret = platform_device_register(&lcdc->fb_pdev);
358 if (ret) {
359 pr_err("%s: Cannot register msm_panel pdev\n", __func__);
360 goto err_plat_dev_reg;
361 }
362
363 pr_info("%s: initialized\n", __func__);
364
365 return 0;
366
367err_plat_dev_reg:
368err_hw_init:
369 platform_set_drvdata(pdev, NULL);
370 clk_put(lcdc->pad_pclk);
371err_get_pad_pclk:
372 clk_put(lcdc->pclk);
373err_get_pclk:
374 clk_put(lcdc->mdp_clk);
375err_get_mdp_clk:
376 kfree(lcdc);
377 return ret;
378}
379
380static int mdp_lcdc_remove(struct platform_device *pdev)
381{
382 struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
383
384 platform_set_drvdata(pdev, NULL);
385
386 clk_put(lcdc->pclk);
387 clk_put(lcdc->pad_pclk);
388 kfree(lcdc);
389
390 return 0;
391}
392
393static struct platform_driver mdp_lcdc_driver = {
394 .probe = mdp_lcdc_probe,
395 .remove = mdp_lcdc_remove,
396 .driver = {
397 .name = "msm_mdp_lcdc",
398 .owner = THIS_MODULE,
399 },
400};
401
402static int mdp_lcdc_add_mdp_device(struct device *dev,
403 struct class_interface *class_intf)
404{
405 /* might need locking if mulitple mdp devices */
406 if (mdp_dev)
407 return 0;
408 mdp_dev = container_of(dev, struct mdp_device, dev);
409 return platform_driver_register(&mdp_lcdc_driver);
410}
411
412static void mdp_lcdc_remove_mdp_device(struct device *dev,
413 struct class_interface *class_intf)
414{
415 /* might need locking if mulitple mdp devices */
416 if (dev != &mdp_dev->dev)
417 return;
418 platform_driver_unregister(&mdp_lcdc_driver);
419 mdp_dev = NULL;
420}
421
422static struct class_interface mdp_lcdc_interface = {
423 .add_dev = &mdp_lcdc_add_mdp_device,
424 .remove_dev = &mdp_lcdc_remove_mdp_device,
425};
426
427static int __init mdp_lcdc_init(void)
428{
429 return register_mdp_client(&mdp_lcdc_interface);
430}
431
432module_init(mdp_lcdc_init);