blob: 9b9ee946a28d7a34c15bec678f16a26979810a38 [file] [log] [blame]
Pavel Machekd480ace2009-09-22 16:47:03 -07001/*
2 * MSM MDDI Transport
3 *
4 * Copyright (C) 2007 Google Incorporated
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
Pavel Machekd480ace2009-09-22 16:47:03 -07006 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Pavel Machekd480ace2009-09-22 16:47:03 -070014 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/kernel.h>
Pavel Machek69fd8d22009-11-11 14:26:51 -080020#include <linux/sched.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021#include <linux/time.h>
22#include <linux/init.h>
23#include <linux/interrupt.h>
24#include <linux/spinlock.h>
25#include <linux/delay.h>
26#include <mach/hardware.h>
27#include <asm/io.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070028
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include <asm/system.h>
30#include <asm/mach-types.h>
31#include <linux/semaphore.h>
32#include <linux/uaccess.h>
33#include <linux/clk.h>
34#include <linux/platform_device.h>
35#include <linux/pm_runtime.h>
36#include "msm_fb.h"
37#include "mddihosti.h"
38#include "mddihost.h"
39#include <mach/gpio.h>
40#include <mach/clk.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070041
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042static int mddi_probe(struct platform_device *pdev);
43static int mddi_remove(struct platform_device *pdev);
Pavel Machekd480ace2009-09-22 16:47:03 -070044
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045static int mddi_off(struct platform_device *pdev);
46static int mddi_on(struct platform_device *pdev);
Pavel Machekd480ace2009-09-22 16:47:03 -070047
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048#ifdef CONFIG_PM
49static int mddi_suspend(struct platform_device *pdev, pm_message_t state);
50static int mddi_resume(struct platform_device *pdev);
51#endif
Pavel Machekd480ace2009-09-22 16:47:03 -070052
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053#ifdef CONFIG_HAS_EARLYSUSPEND
54static void mddi_early_suspend(struct early_suspend *h);
55static void mddi_early_resume(struct early_suspend *h);
56#endif
Pavel Machekd480ace2009-09-22 16:47:03 -070057
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058static void pmdh_clk_disable(void);
59static void pmdh_clk_enable(void);
60static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
61static int pdev_list_cnt;
62static struct clk *mddi_clk;
63static struct clk *mddi_pclk;
64static struct mddi_platform_data *mddi_pdata;
Pavel Machekd480ace2009-09-22 16:47:03 -070065
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066DEFINE_MUTEX(mddi_timer_lock);
Pavel Machekd480ace2009-09-22 16:47:03 -070067
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068static int mddi_runtime_suspend(struct device *dev)
Pavel Machekd480ace2009-09-22 16:47:03 -070069{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070 dev_dbg(dev, "pm_runtime: suspending...\n");
Pavel Machekd480ace2009-09-22 16:47:03 -070071 return 0;
72}
73
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074static int mddi_runtime_resume(struct device *dev)
Pavel Machekd480ace2009-09-22 16:47:03 -070075{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076 dev_dbg(dev, "pm_runtime: resuming...\n");
Pavel Machekd480ace2009-09-22 16:47:03 -070077 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -070078}
79
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080static int mddi_runtime_idle(struct device *dev)
81{
82 dev_dbg(dev, "pm_runtime: idling...\n");
83 return 0;
84}
85
86static struct dev_pm_ops mddi_dev_pm_ops = {
87 .runtime_suspend = mddi_runtime_suspend,
88 .runtime_resume = mddi_runtime_resume,
89 .runtime_idle = mddi_runtime_idle,
90};
91
92static int pmdh_clk_status;
93int irq_enabled;
94unsigned char mddi_timer_shutdown_flag;
Pavel Machekd480ace2009-09-22 16:47:03 -070095
96static struct platform_driver mddi_driver = {
97 .probe = mddi_probe,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 .remove = mddi_remove,
99#ifndef CONFIG_HAS_EARLYSUSPEND
100#ifdef CONFIG_PM
101 .suspend = mddi_suspend,
102 .resume = mddi_resume,
103#endif
104#endif
105 .shutdown = NULL,
106 .driver = {
107 .name = "mddi",
108 .pm = &mddi_dev_pm_ops,
109 },
Pavel Machekd480ace2009-09-22 16:47:03 -0700110};
111
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112extern int int_mddi_pri_flag;
113DEFINE_MUTEX(pmdh_clk_lock);
114
115int pmdh_clk_func(int value)
116{
117 int ret = 0;
118
119 switch (value) {
120 case 0:
121 pmdh_clk_disable();
122 break;
123 case 1:
124 pmdh_clk_enable();
125 break;
126 case 2:
127 default:
128 mutex_lock(&pmdh_clk_lock);
129 ret = pmdh_clk_status;
130 mutex_unlock(&pmdh_clk_lock);
131 break;
132 }
133 return ret;
134}
135
136static void pmdh_clk_disable()
137{
138 mutex_lock(&pmdh_clk_lock);
139 if (pmdh_clk_status == 0) {
140 mutex_unlock(&pmdh_clk_lock);
141 return;
142 }
143
144 if (mddi_host_timer.function) {
145 mutex_lock(&mddi_timer_lock);
146 mddi_timer_shutdown_flag = 1;
147 mutex_unlock(&mddi_timer_lock);
148 del_timer_sync(&mddi_host_timer);
149 mutex_lock(&mddi_timer_lock);
150 mddi_timer_shutdown_flag = 0;
151 mutex_unlock(&mddi_timer_lock);
152 }
153 if (int_mddi_pri_flag && irq_enabled) {
154 disable_irq(INT_MDDI_PRI);
155 irq_enabled = 0;
156 }
157
158 if (mddi_clk) {
159 clk_disable(mddi_clk);
160 pmdh_clk_status = 0;
161 }
162 if (mddi_pclk)
163 clk_disable(mddi_pclk);
164 mutex_unlock(&pmdh_clk_lock);
165}
166
167static void pmdh_clk_enable()
168{
169 mutex_lock(&pmdh_clk_lock);
170 if (pmdh_clk_status == 1) {
171 mutex_unlock(&pmdh_clk_lock);
172 return;
173 }
174
175 if (mddi_clk) {
176 clk_enable(mddi_clk);
177 pmdh_clk_status = 1;
178 }
179 if (mddi_pclk)
180 clk_enable(mddi_pclk);
181
182 if (int_mddi_pri_flag && !irq_enabled) {
183 enable_irq(INT_MDDI_PRI);
184 irq_enabled = 1;
185 }
186
187 if (mddi_host_timer.function)
188 mddi_host_timer_service(0);
189
190 mutex_unlock(&pmdh_clk_lock);
191}
192
193static int mddi_off(struct platform_device *pdev)
194{
195 struct msm_fb_data_type *mfd;
196 boolean dma_pending, dma_update_flag;
197 int ret, i;
198
199 mfd = platform_get_drvdata(pdev);
200
201 for (i = 0; i < 6; i++) {
202 dma_update_flag = mfd->dma_update_flag;
203 dma_pending = mfd->dma->busy;
204 if (dma_update_flag && !dma_pending)
205 break;
206 msleep(5);
207 }
208
209 pmdh_clk_enable();
210 ret = panel_next_off(pdev);
211 pmdh_clk_disable();
212
213 if (mddi_pdata && mddi_pdata->mddi_power_save)
214 mddi_pdata->mddi_power_save(0);
215#ifdef CONFIG_MSM_BUS_SCALING
216 mdp_bus_scale_update_request(0);
217#else
218 if (mfd->ebi1_clk)
219 clk_disable(mfd->ebi1_clk);
220#endif
221 pm_runtime_put(&pdev->dev);
222 return ret;
223}
224
225static int mddi_on(struct platform_device *pdev)
226{
227 int ret = 0;
228 u32 clk_rate;
229 struct msm_fb_data_type *mfd;
230#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION
231 mddi_host_type host_idx = MDDI_HOST_PRIM;
232 u32 stat_reg;
233#endif
234
235 mfd = platform_get_drvdata(pdev);
236 pm_runtime_get(&pdev->dev);
237 if (mddi_pdata && mddi_pdata->mddi_power_save)
238 mddi_pdata->mddi_power_save(1);
239
240 pmdh_clk_enable();
241#ifdef ENABLE_FWD_LINK_SKEW_CALIBRATION
242 if (mddi_client_type < 2) {
243 /* For skew calibration, clock should be less than 50MHz */
244 if (!clk_set_min_rate(mddi_clk, 49000000)) {
245 stat_reg = mddi_host_reg_in(STAT);
246 printk(KERN_DEBUG "\n stat_reg = 0x%x", stat_reg);
247 mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
248 if (stat_reg & (0x1 << 4))
249 mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
250
251 mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
252 mddi_send_fw_link_skew_cal(host_idx);
253 mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
254 mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
255 } else {
256 printk(KERN_ERR "%s: clk_set_min_rate failed\n",
257 __func__);
258 }
259 }
260#endif
261
262 clk_rate = mfd->fbi->var.pixclock;
263 clk_rate = min(clk_rate, mfd->panel_info.clk_max);
264
265 if (mddi_pdata &&
266 mddi_pdata->mddi_sel_clk &&
267 mddi_pdata->mddi_sel_clk(&clk_rate))
268 printk(KERN_ERR
269 "%s: can't select mddi io clk targate rate = %d\n",
270 __func__, clk_rate);
271
272 if (clk_set_min_rate(mddi_clk, clk_rate) < 0)
273 printk(KERN_ERR "%s: clk_set_min_rate failed\n",
274 __func__);
275
276#ifdef CONFIG_MSM_BUS_SCALING
277 mdp_bus_scale_update_request(2);
278#else
279 if (mfd->ebi1_clk)
280 clk_enable(mfd->ebi1_clk);
281#endif
282 ret = panel_next_on(pdev);
283
284 return ret;
285}
286
287static int mddi_resource_initialized;
288
289static int mddi_probe(struct platform_device *pdev)
290{
291 struct msm_fb_data_type *mfd;
292 struct platform_device *mdp_dev = NULL;
293 struct msm_fb_panel_data *pdata = NULL;
294 int rc;
295 resource_size_t size ;
296 u32 clk_rate;
297
298 if ((pdev->id == 0) && (pdev->num_resources >= 0)) {
299 mddi_pdata = pdev->dev.platform_data;
300
301 size = resource_size(&pdev->resource[0]);
302 msm_pmdh_base = ioremap(pdev->resource[0].start, size);
303
304 MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n",
305 pdev->resource[0].start, (int) msm_pmdh_base);
306
307 if (unlikely(!msm_pmdh_base))
308 return -ENOMEM;
309
310 if (mddi_pdata && mddi_pdata->mddi_power_save)
311 mddi_pdata->mddi_power_save(1);
312
313 mddi_resource_initialized = 1;
314 return 0;
315 }
316
317 if (!mddi_resource_initialized)
318 return -EPERM;
319
320 mfd = platform_get_drvdata(pdev);
321
322 if (!mfd)
323 return -ENODEV;
324
325 if (mfd->key != MFD_KEY)
326 return -EINVAL;
327
328 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
329 return -ENOMEM;
330
331 mdp_dev = platform_device_alloc("mdp", pdev->id);
332 if (!mdp_dev)
333 return -ENOMEM;
334
335 /*
336 * link to the latest pdev
337 */
338 mfd->pdev = mdp_dev;
339 mfd->dest = DISPLAY_LCD;
340
341 /*
342 * alloc panel device data
343 */
344 if (platform_device_add_data
345 (mdp_dev, pdev->dev.platform_data,
346 sizeof(struct msm_fb_panel_data))) {
347 printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n");
348 platform_device_put(mdp_dev);
349 return -ENOMEM;
350 }
351 /*
352 * data chain
353 */
354 pdata = mdp_dev->dev.platform_data;
355 pdata->on = mddi_on;
356 pdata->off = mddi_off;
357 pdata->next = pdev;
358 pdata->clk_func = pmdh_clk_func;
359 /*
360 * get/set panel specific fb info
361 */
362 mfd->panel_info = pdata->panel_info;
363
364 if (mfd->index == 0)
365 mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
366 else
367 mfd->fb_imgType = MDP_RGB_565;
368
369 clk_rate = mfd->panel_info.clk_max;
370 if (mddi_pdata &&
371 mddi_pdata->mddi_sel_clk &&
372 mddi_pdata->mddi_sel_clk(&clk_rate))
373 printk(KERN_ERR
374 "%s: can't select mddi io clk targate rate = %d\n",
375 __func__, clk_rate);
376
377 if (clk_set_max_rate(mddi_clk, clk_rate) < 0)
378 printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__);
379 mfd->panel_info.clk_rate = mfd->panel_info.clk_min;
380
381 if (!mddi_client_type)
382 mddi_client_type = mfd->panel_info.lcd.rev;
383 else if (!mfd->panel_info.lcd.rev)
384 printk(KERN_ERR
385 "%s: mddi client is trying to revert back to type 1 !!!\n",
386 __func__);
387
388 /*
389 * set driver data
390 */
391 platform_set_drvdata(mdp_dev, mfd);
392 rc = pm_runtime_set_active(&pdev->dev);
393 if (rc < 0)
394 printk(KERN_ERR "pm_runtime: fail to set active\n");
395
396 rc = 0;
397 pm_runtime_enable(&pdev->dev);
398#ifndef CONFIG_MSM_BUS_SCALING
399 mfd->ebi1_clk = clk_get(NULL, "ebi1_mddi_clk");
400 if (IS_ERR(mfd->ebi1_clk))
401 return PTR_ERR(mfd->ebi1_clk);
402 clk_set_rate(mfd->ebi1_clk, 65000000);
403#endif
404 /*
405 * register in mdp driver
406 */
407 rc = platform_device_add(mdp_dev);
408 if (rc)
409 goto mddi_probe_err;
410
411 pdev_list[pdev_list_cnt++] = pdev;
412
413#ifdef CONFIG_HAS_EARLYSUSPEND
414 mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
415 mfd->mddi_early_suspend.suspend = mddi_early_suspend;
416 mfd->mddi_early_suspend.resume = mddi_early_resume;
417 register_early_suspend(&mfd->mddi_early_suspend);
418#endif
419
420 return 0;
421
422mddi_probe_err:
423 platform_device_put(mdp_dev);
424 return rc;
425}
426
427static int mddi_pad_ctrl;
428static int mddi_power_locked;
429
430int mddi_client_power(unsigned int client_id)
431{
432 int ret = 0;
433 if (mddi_pdata && mddi_pdata->mddi_client_power)
434 ret = mddi_pdata->mddi_client_power(client_id);
435 return ret;
436}
437
438void mddi_disable(int lock)
439{
440 mddi_host_type host_idx = MDDI_HOST_PRIM;
441
442 if (mddi_power_locked)
443 return;
444
445 if (lock)
446 mddi_power_locked = 1;
447 pmdh_clk_enable();
448
449 mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
450 mddi_host_reg_out(PAD_CTL, 0x0);
451
452 if (clk_set_min_rate(mddi_clk, 0) < 0)
453 printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__);
454
455 pmdh_clk_disable();
456
457 if (mddi_pdata && mddi_pdata->mddi_power_save)
458 mddi_pdata->mddi_power_save(0);
459}
460
461#ifdef CONFIG_PM
462static int mddi_is_in_suspend;
463
464static int mddi_suspend(struct platform_device *pdev, pm_message_t state)
465{
466 mddi_host_type host_idx = MDDI_HOST_PRIM;
467 if (mddi_is_in_suspend)
468 return 0;
469
470 mddi_is_in_suspend = 1;
471
472 if (mddi_power_locked)
473 return 0;
474
475 pmdh_clk_enable();
476
477 mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
478 mddi_host_reg_out(PAD_CTL, 0x0);
479
480 if (clk_set_min_rate(mddi_clk, 0) < 0)
481 printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__);
482
483 pmdh_clk_disable();
484
485 return 0;
486}
487
488static int mddi_resume(struct platform_device *pdev)
489{
490 mddi_host_type host_idx = MDDI_HOST_PRIM;
491
492 if (!mddi_is_in_suspend)
493 return 0;
494
495 mddi_is_in_suspend = 0;
496
497 if (mddi_power_locked)
498 return 0;
499
500 pmdh_clk_enable();
501
502 mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl);
503
504
505 return 0;
506}
507#endif
508
509#ifdef CONFIG_HAS_EARLYSUSPEND
510static void mddi_early_suspend(struct early_suspend *h)
511{
512 pm_message_t state;
513 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
514 mddi_early_suspend);
515
516 state.event = PM_EVENT_SUSPEND;
517 mddi_suspend(mfd->pdev, state);
518}
519
520static void mddi_early_resume(struct early_suspend *h)
521{
522 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
523 mddi_early_suspend);
524 mddi_resume(mfd->pdev);
525}
526#endif
527
528static int mddi_remove(struct platform_device *pdev)
529{
530 pm_runtime_disable(&pdev->dev);
531 if (mddi_host_timer.function) {
532 mutex_lock(&mddi_timer_lock);
533 mddi_timer_shutdown_flag = 1;
534 mutex_unlock(&mddi_timer_lock);
535 del_timer_sync(&mddi_host_timer);
536 mutex_lock(&mddi_timer_lock);
537 mddi_timer_shutdown_flag = 0;
538 mutex_unlock(&mddi_timer_lock);
539 }
540
541 iounmap(msm_pmdh_base);
542
543 return 0;
544}
545
546static int mddi_register_driver(void)
Pavel Machekd480ace2009-09-22 16:47:03 -0700547{
548 return platform_driver_register(&mddi_driver);
549}
550
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551static int __init mddi_driver_init(void)
552{
553 int ret;
554 pmdh_clk_status = 0;
555
556 mddi_clk = clk_get(NULL, "mddi_clk");
557 if (IS_ERR(mddi_clk)) {
558 printk(KERN_ERR "can't find mddi_clk\n");
559 return PTR_ERR(mddi_clk);
560 }
561 ret = clk_set_min_rate(mddi_clk, 49000000);
562 if (ret)
563 printk(KERN_ERR "Can't set mddi_clk min rate to 49000000\n");
564
565 printk(KERN_INFO "mddi_clk init rate is %lu\n",
566 clk_get_rate(mddi_clk));
567 mddi_pclk = clk_get(NULL, "mddi_pclk");
568 if (IS_ERR(mddi_pclk))
569 mddi_pclk = NULL;
570 pmdh_clk_enable();
571
572 ret = mddi_register_driver();
573 if (ret) {
574 pmdh_clk_disable();
575 clk_put(mddi_clk);
576 if (mddi_pclk)
577 clk_put(mddi_pclk);
578 printk(KERN_ERR "mddi_register_driver() failed!\n");
579 return ret;
580 }
581
582 mddi_init();
583
584 return ret;
585}
586
587module_init(mddi_driver_init);