1/* Copyright (c) 2008-2009, Code Aurora Forum. 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 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15 * 02110-1301, USA. 16 */ 17 18#include <linux/module.h> 19#include <linux/kernel.h> 20#include <linux/sched.h> 21#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> 28 29#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 <mach/clk.h> 35#include <linux/platform_device.h> 36 37#include "msm_fb.h" 38#include "mddihosti.h" 39 40static int mddi_ext_probe(struct platform_device *pdev); 41static int mddi_ext_remove(struct platform_device *pdev); 42 43static int mddi_ext_off(struct platform_device *pdev); 44static int mddi_ext_on(struct platform_device *pdev); 45 46static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; 47static int pdev_list_cnt; 48 49static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state); 50static int mddi_ext_resume(struct platform_device *pdev); 51 52#ifdef CONFIG_HAS_EARLYSUSPEND 53static void mddi_ext_early_suspend(struct early_suspend *h); 54static void mddi_ext_early_resume(struct early_suspend *h); 55#endif 56 57static struct platform_driver mddi_ext_driver = { 58 .probe = mddi_ext_probe, 59 .remove = mddi_ext_remove, 60#ifndef CONFIG_HAS_EARLYSUSPEND 61#ifdef CONFIG_PM 62 .suspend = mddi_ext_suspend, 63 .resume = mddi_ext_resume, 64#endif 65#endif 66 .resume_early = NULL, 67 .resume = NULL, 68 .shutdown = NULL, 69 .driver = { 70 .name = "mddi_ext", 71 }, 72}; 73 74static struct clk *mddi_ext_clk; 75static struct mddi_platform_data *mddi_ext_pdata; 76 77extern int int_mddi_ext_flag; 78 79static int mddi_ext_off(struct platform_device *pdev) 80{ 81 int ret = 0; 82 83 ret = panel_next_off(pdev); 84 mddi_host_stop_ext_display(); 85 86 return ret; 87} 88 89static int mddi_ext_on(struct platform_device *pdev) 90{ 91 int ret = 0; 92 u32 clk_rate; 93 struct msm_fb_data_type *mfd; 94 95 mfd = platform_get_drvdata(pdev); 96 97 clk_rate = mfd->fbi->var.pixclock; 98 clk_rate = min(clk_rate, mfd->panel_info.clk_max); 99 100 if (mddi_ext_pdata && 101 mddi_ext_pdata->mddi_sel_clk && 102 mddi_ext_pdata->mddi_sel_clk(&clk_rate)) 103 printk(KERN_ERR 104 "%s: can't select mddi io clk targate rate = %d\n", 105 __func__, clk_rate); 106 107 if (clk_set_min_rate(mddi_ext_clk, clk_rate) < 0) 108 printk(KERN_ERR "%s: clk_set_min_rate failed\n", 109 __func__); 110 111 mddi_host_start_ext_display(); 112 ret = panel_next_on(pdev); 113 114 return ret; 115} 116 117static int mddi_ext_resource_initialized; 118 119static int mddi_ext_probe(struct platform_device *pdev) 120{ 121 struct msm_fb_data_type *mfd; 122 struct platform_device *mdp_dev = NULL; 123 struct msm_fb_panel_data *pdata = NULL; 124 int rc; 125 resource_size_t size ; 126 u32 clk_rate; 127 128 if ((pdev->id == 0) && (pdev->num_resources >= 0)) { 129 mddi_ext_pdata = pdev->dev.platform_data; 130 131 size = resource_size(&pdev->resource[0]); 132 msm_emdh_base = ioremap(pdev->resource[0].start, size); 133 134 MSM_FB_INFO("external mddi base address = 0x%x\n", 135 pdev->resource[0].start); 136 137 if (unlikely(!msm_emdh_base)) 138 return -ENOMEM; 139 140 mddi_ext_resource_initialized = 1; 141 return 0; 142 } 143 144 if (!mddi_ext_resource_initialized) 145 return -EPERM; 146 147 mfd = platform_get_drvdata(pdev); 148 149 if (!mfd) 150 return -ENODEV; 151 152 if (mfd->key != MFD_KEY) 153 return -EINVAL; 154 155 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) 156 return -ENOMEM; 157 158 mdp_dev = platform_device_alloc("mdp", pdev->id); 159 if (!mdp_dev) 160 return -ENOMEM; 161 162 /* 163 * link to the latest pdev 164 */ 165 mfd->pdev = mdp_dev; 166 mfd->dest = DISPLAY_EXT_MDDI; 167 168 /* 169 * alloc panel device data 170 */ 171 if (platform_device_add_data 172 (mdp_dev, pdev->dev.platform_data, 173 sizeof(struct msm_fb_panel_data))) { 174 printk(KERN_ERR "mddi_ext_probe: platform_device_add_data failed!\n"); 175 platform_device_put(mdp_dev); 176 return -ENOMEM; 177 } 178 /* 179 * data chain 180 */ 181 pdata = mdp_dev->dev.platform_data; 182 pdata->on = mddi_ext_on; 183 pdata->off = mddi_ext_off; 184 pdata->next = pdev; 185 186 /* 187 * get/set panel specific fb info 188 */ 189 mfd->panel_info = pdata->panel_info; 190 mfd->fb_imgType = MDP_RGB_565; 191 192 clk_rate = mfd->panel_info.clk_max; 193 if (mddi_ext_pdata && 194 mddi_ext_pdata->mddi_sel_clk && 195 mddi_ext_pdata->mddi_sel_clk(&clk_rate)) 196 printk(KERN_ERR 197 "%s: can't select mddi io clk targate rate = %d\n", 198 __func__, clk_rate); 199 200 if (clk_set_max_rate(mddi_ext_clk, clk_rate) < 0) 201 printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__); 202 mfd->panel_info.clk_rate = mfd->panel_info.clk_min; 203 204 /* 205 * set driver data 206 */ 207 platform_set_drvdata(mdp_dev, mfd); 208 209 /* 210 * register in mdp driver 211 */ 212 rc = platform_device_add(mdp_dev); 213 if (rc) 214 goto mddi_ext_probe_err; 215 216 pdev_list[pdev_list_cnt++] = pdev; 217 218#ifdef CONFIG_HAS_EARLYSUSPEND 219 mfd->mddi_ext_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; 220 mfd->mddi_ext_early_suspend.suspend = mddi_ext_early_suspend; 221 mfd->mddi_ext_early_suspend.resume = mddi_ext_early_resume; 222 register_early_suspend(&mfd->mddi_ext_early_suspend); 223#endif 224 225 return 0; 226 227mddi_ext_probe_err: 228 platform_device_put(mdp_dev); 229 return rc; 230} 231 232static int mddi_ext_is_in_suspend; 233 234static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state) 235{ 236 if (mddi_ext_is_in_suspend) 237 return 0; 238 239 mddi_ext_is_in_suspend = 1; 240 241 if (clk_set_min_rate(mddi_ext_clk, 0) < 0) 242 printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__); 243 244 clk_disable(mddi_ext_clk); 245 disable_irq(INT_MDDI_EXT); 246 247 return 0; 248} 249 250static int mddi_ext_resume(struct platform_device *pdev) 251{ 252 struct msm_fb_data_type *mfd; 253 254 mfd = platform_get_drvdata(pdev); 255 256 if (!mddi_ext_is_in_suspend) 257 return 0; 258 259 mddi_ext_is_in_suspend = 0; 260 enable_irq(INT_MDDI_EXT); 261 262 clk_enable(mddi_ext_clk); 263 264 return 0; 265} 266 267#ifdef CONFIG_HAS_EARLYSUSPEND 268static void mddi_ext_early_suspend(struct early_suspend *h) 269{ 270 pm_message_t state; 271 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, 272 mddi_ext_early_suspend); 273 274 state.event = PM_EVENT_SUSPEND; 275 mddi_ext_suspend(mfd->pdev, state); 276} 277 278static void mddi_ext_early_resume(struct early_suspend *h) 279{ 280 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, 281 mddi_ext_early_suspend); 282 mddi_ext_resume(mfd->pdev); 283} 284#endif 285 286static int mddi_ext_remove(struct platform_device *pdev) 287{ 288 iounmap(msm_emdh_base); 289 return 0; 290} 291 292static int mddi_ext_register_driver(void) 293{ 294 return platform_driver_register(&mddi_ext_driver); 295} 296 297static int __init mddi_ext_driver_init(void) 298{ 299 int ret; 300 301 mddi_ext_clk = clk_get(NULL, "emdh_clk"); 302 if (IS_ERR(mddi_ext_clk)) { 303 printk(KERN_ERR "can't find emdh_clk\n"); 304 return PTR_ERR(mddi_ext_clk); 305 } 306 clk_enable(mddi_ext_clk); 307 308 ret = mddi_ext_register_driver(); 309 if (ret) { 310 clk_disable(mddi_ext_clk); 311 clk_put(mddi_ext_clk); 312 printk(KERN_ERR "mddi_ext_register_driver() failed!\n"); 313 return ret; 314 } 315 mddi_init(); 316 317 return ret; 318} 319 320module_init(mddi_ext_driver_init); 321