blob: c6f9fb2f3763d293418f6cb6cc42ef5c7da120eb [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 */
Matt Wagantalla12cc952011-11-08 18:14:50 -0800244 clk_rate = clk_round_rate(mddi_clk, 49000000);
245 if (!clk_set_rate(mddi_clk, clk_rate)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246 stat_reg = mddi_host_reg_in(STAT);
247 printk(KERN_DEBUG "\n stat_reg = 0x%x", stat_reg);
248 mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE);
249 if (stat_reg & (0x1 << 4))
250 mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE);
251
252 mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
253 mddi_send_fw_link_skew_cal(host_idx);
254 mddi_host_reg_out(CMD, MDDI_CMD_SEND_RTD);
255 mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1);
256 } else {
Matt Wagantalla12cc952011-11-08 18:14:50 -0800257 printk(KERN_ERR "%s: clk_set_rate failed\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 __func__);
259 }
260 }
261#endif
262
263 clk_rate = mfd->fbi->var.pixclock;
264 clk_rate = min(clk_rate, mfd->panel_info.clk_max);
265
266 if (mddi_pdata &&
267 mddi_pdata->mddi_sel_clk &&
268 mddi_pdata->mddi_sel_clk(&clk_rate))
269 printk(KERN_ERR
270 "%s: can't select mddi io clk targate rate = %d\n",
271 __func__, clk_rate);
272
Matt Wagantalla12cc952011-11-08 18:14:50 -0800273 clk_rate = clk_round_rate(mddi_clk, clk_rate);
274 if (clk_set_rate(mddi_clk, clk_rate) < 0)
275 printk(KERN_ERR "%s: clk_set_rate failed\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276 __func__);
277
278#ifdef CONFIG_MSM_BUS_SCALING
279 mdp_bus_scale_update_request(2);
280#else
281 if (mfd->ebi1_clk)
282 clk_enable(mfd->ebi1_clk);
283#endif
284 ret = panel_next_on(pdev);
285
286 return ret;
287}
288
289static int mddi_resource_initialized;
290
291static int mddi_probe(struct platform_device *pdev)
292{
293 struct msm_fb_data_type *mfd;
294 struct platform_device *mdp_dev = NULL;
295 struct msm_fb_panel_data *pdata = NULL;
296 int rc;
297 resource_size_t size ;
298 u32 clk_rate;
299
300 if ((pdev->id == 0) && (pdev->num_resources >= 0)) {
301 mddi_pdata = pdev->dev.platform_data;
302
303 size = resource_size(&pdev->resource[0]);
304 msm_pmdh_base = ioremap(pdev->resource[0].start, size);
305
306 MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n",
307 pdev->resource[0].start, (int) msm_pmdh_base);
308
309 if (unlikely(!msm_pmdh_base))
310 return -ENOMEM;
311
312 if (mddi_pdata && mddi_pdata->mddi_power_save)
313 mddi_pdata->mddi_power_save(1);
314
315 mddi_resource_initialized = 1;
316 return 0;
317 }
318
319 if (!mddi_resource_initialized)
320 return -EPERM;
321
322 mfd = platform_get_drvdata(pdev);
323
324 if (!mfd)
325 return -ENODEV;
326
327 if (mfd->key != MFD_KEY)
328 return -EINVAL;
329
330 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
331 return -ENOMEM;
332
333 mdp_dev = platform_device_alloc("mdp", pdev->id);
334 if (!mdp_dev)
335 return -ENOMEM;
336
337 /*
338 * link to the latest pdev
339 */
340 mfd->pdev = mdp_dev;
341 mfd->dest = DISPLAY_LCD;
342
343 /*
344 * alloc panel device data
345 */
346 if (platform_device_add_data
347 (mdp_dev, pdev->dev.platform_data,
348 sizeof(struct msm_fb_panel_data))) {
349 printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n");
350 platform_device_put(mdp_dev);
351 return -ENOMEM;
352 }
353 /*
354 * data chain
355 */
356 pdata = mdp_dev->dev.platform_data;
357 pdata->on = mddi_on;
358 pdata->off = mddi_off;
359 pdata->next = pdev;
360 pdata->clk_func = pmdh_clk_func;
361 /*
362 * get/set panel specific fb info
363 */
364 mfd->panel_info = pdata->panel_info;
365
366 if (mfd->index == 0)
367 mfd->fb_imgType = MSMFB_DEFAULT_TYPE;
368 else
369 mfd->fb_imgType = MDP_RGB_565;
370
371 clk_rate = mfd->panel_info.clk_max;
372 if (mddi_pdata &&
373 mddi_pdata->mddi_sel_clk &&
374 mddi_pdata->mddi_sel_clk(&clk_rate))
375 printk(KERN_ERR
376 "%s: can't select mddi io clk targate rate = %d\n",
377 __func__, clk_rate);
378
379 if (clk_set_max_rate(mddi_clk, clk_rate) < 0)
380 printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__);
381 mfd->panel_info.clk_rate = mfd->panel_info.clk_min;
382
383 if (!mddi_client_type)
384 mddi_client_type = mfd->panel_info.lcd.rev;
385 else if (!mfd->panel_info.lcd.rev)
386 printk(KERN_ERR
387 "%s: mddi client is trying to revert back to type 1 !!!\n",
388 __func__);
389
390 /*
391 * set driver data
392 */
393 platform_set_drvdata(mdp_dev, mfd);
394 rc = pm_runtime_set_active(&pdev->dev);
395 if (rc < 0)
396 printk(KERN_ERR "pm_runtime: fail to set active\n");
397
398 rc = 0;
399 pm_runtime_enable(&pdev->dev);
400#ifndef CONFIG_MSM_BUS_SCALING
401 mfd->ebi1_clk = clk_get(NULL, "ebi1_mddi_clk");
402 if (IS_ERR(mfd->ebi1_clk))
403 return PTR_ERR(mfd->ebi1_clk);
404 clk_set_rate(mfd->ebi1_clk, 65000000);
405#endif
406 /*
407 * register in mdp driver
408 */
409 rc = platform_device_add(mdp_dev);
410 if (rc)
411 goto mddi_probe_err;
412
413 pdev_list[pdev_list_cnt++] = pdev;
414
415#ifdef CONFIG_HAS_EARLYSUSPEND
416 mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
417 mfd->mddi_early_suspend.suspend = mddi_early_suspend;
418 mfd->mddi_early_suspend.resume = mddi_early_resume;
419 register_early_suspend(&mfd->mddi_early_suspend);
420#endif
421
422 return 0;
423
424mddi_probe_err:
425 platform_device_put(mdp_dev);
426 return rc;
427}
428
429static int mddi_pad_ctrl;
430static int mddi_power_locked;
431
432int mddi_client_power(unsigned int client_id)
433{
434 int ret = 0;
435 if (mddi_pdata && mddi_pdata->mddi_client_power)
436 ret = mddi_pdata->mddi_client_power(client_id);
437 return ret;
438}
439
440void mddi_disable(int lock)
441{
442 mddi_host_type host_idx = MDDI_HOST_PRIM;
443
444 if (mddi_power_locked)
445 return;
446
447 if (lock)
448 mddi_power_locked = 1;
449 pmdh_clk_enable();
450
451 mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
452 mddi_host_reg_out(PAD_CTL, 0x0);
453
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454 pmdh_clk_disable();
455
456 if (mddi_pdata && mddi_pdata->mddi_power_save)
457 mddi_pdata->mddi_power_save(0);
458}
459
460#ifdef CONFIG_PM
461static int mddi_is_in_suspend;
462
463static int mddi_suspend(struct platform_device *pdev, pm_message_t state)
464{
465 mddi_host_type host_idx = MDDI_HOST_PRIM;
466 if (mddi_is_in_suspend)
467 return 0;
468
469 mddi_is_in_suspend = 1;
470
471 if (mddi_power_locked)
472 return 0;
473
474 pmdh_clk_enable();
475
476 mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL);
477 mddi_host_reg_out(PAD_CTL, 0x0);
478
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479 pmdh_clk_disable();
480
481 return 0;
482}
483
484static int mddi_resume(struct platform_device *pdev)
485{
486 mddi_host_type host_idx = MDDI_HOST_PRIM;
487
488 if (!mddi_is_in_suspend)
489 return 0;
490
491 mddi_is_in_suspend = 0;
492
493 if (mddi_power_locked)
494 return 0;
495
496 pmdh_clk_enable();
497
498 mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl);
499
500
501 return 0;
502}
503#endif
504
505#ifdef CONFIG_HAS_EARLYSUSPEND
506static void mddi_early_suspend(struct early_suspend *h)
507{
508 pm_message_t state;
509 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
510 mddi_early_suspend);
511
512 state.event = PM_EVENT_SUSPEND;
513 mddi_suspend(mfd->pdev, state);
514}
515
516static void mddi_early_resume(struct early_suspend *h)
517{
518 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
519 mddi_early_suspend);
520 mddi_resume(mfd->pdev);
521}
522#endif
523
524static int mddi_remove(struct platform_device *pdev)
525{
526 pm_runtime_disable(&pdev->dev);
527 if (mddi_host_timer.function) {
528 mutex_lock(&mddi_timer_lock);
529 mddi_timer_shutdown_flag = 1;
530 mutex_unlock(&mddi_timer_lock);
531 del_timer_sync(&mddi_host_timer);
532 mutex_lock(&mddi_timer_lock);
533 mddi_timer_shutdown_flag = 0;
534 mutex_unlock(&mddi_timer_lock);
535 }
536
537 iounmap(msm_pmdh_base);
538
539 return 0;
540}
541
542static int mddi_register_driver(void)
Pavel Machekd480ace2009-09-22 16:47:03 -0700543{
544 return platform_driver_register(&mddi_driver);
545}
546
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547static int __init mddi_driver_init(void)
548{
549 int ret;
Matt Wagantalla12cc952011-11-08 18:14:50 -0800550 unsigned long rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 pmdh_clk_status = 0;
552
553 mddi_clk = clk_get(NULL, "mddi_clk");
554 if (IS_ERR(mddi_clk)) {
555 printk(KERN_ERR "can't find mddi_clk\n");
556 return PTR_ERR(mddi_clk);
557 }
Matt Wagantalla12cc952011-11-08 18:14:50 -0800558 rate = clk_round_rate(mddi_clk, 49000000);
559 ret = clk_set_rate(mddi_clk, rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 if (ret)
Matt Wagantalla12cc952011-11-08 18:14:50 -0800561 printk(KERN_ERR "Can't set mddi_clk min rate to %lu\n", rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562
563 printk(KERN_INFO "mddi_clk init rate is %lu\n",
564 clk_get_rate(mddi_clk));
565 mddi_pclk = clk_get(NULL, "mddi_pclk");
566 if (IS_ERR(mddi_pclk))
567 mddi_pclk = NULL;
568 pmdh_clk_enable();
569
570 ret = mddi_register_driver();
571 if (ret) {
572 pmdh_clk_disable();
573 clk_put(mddi_clk);
574 if (mddi_pclk)
575 clk_put(mddi_pclk);
576 printk(KERN_ERR "mddi_register_driver() failed!\n");
577 return ret;
578 }
579
580 mddi_init();
581
582 return ret;
583}
584
585module_init(mddi_driver_init);