Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.7 386 lines 8.0 kB view raw
1/* 2 * TFP410 DPI-to-DVI chip 3 * 4 * Copyright (C) 2011 Texas Instruments Inc 5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include <linux/module.h> 21#include <linux/slab.h> 22#include <video/omapdss.h> 23#include <linux/i2c.h> 24#include <linux/gpio.h> 25#include <drm/drm_edid.h> 26 27#include <video/omap-panel-tfp410.h> 28 29static const struct omap_video_timings tfp410_default_timings = { 30 .x_res = 640, 31 .y_res = 480, 32 33 .pixel_clock = 23500, 34 35 .hfp = 48, 36 .hsw = 32, 37 .hbp = 80, 38 39 .vfp = 3, 40 .vsw = 4, 41 .vbp = 7, 42 43 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, 44 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, 45 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 46 .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 47 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, 48}; 49 50struct panel_drv_data { 51 struct omap_dss_device *dssdev; 52 53 struct mutex lock; 54 55 int pd_gpio; 56 57 struct i2c_adapter *i2c_adapter; 58}; 59 60static int tfp410_power_on(struct omap_dss_device *dssdev) 61{ 62 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 63 int r; 64 65 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) 66 return 0; 67 68 omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings); 69 omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines); 70 71 r = omapdss_dpi_display_enable(dssdev); 72 if (r) 73 goto err0; 74 75 if (gpio_is_valid(ddata->pd_gpio)) 76 gpio_set_value_cansleep(ddata->pd_gpio, 1); 77 78 return 0; 79err0: 80 return r; 81} 82 83static void tfp410_power_off(struct omap_dss_device *dssdev) 84{ 85 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 86 87 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) 88 return; 89 90 if (gpio_is_valid(ddata->pd_gpio)) 91 gpio_set_value_cansleep(ddata->pd_gpio, 0); 92 93 omapdss_dpi_display_disable(dssdev); 94} 95 96static int tfp410_probe(struct omap_dss_device *dssdev) 97{ 98 struct panel_drv_data *ddata; 99 int r; 100 int i2c_bus_num; 101 102 ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL); 103 if (!ddata) 104 return -ENOMEM; 105 106 dssdev->panel.timings = tfp410_default_timings; 107 108 ddata->dssdev = dssdev; 109 mutex_init(&ddata->lock); 110 111 if (dssdev->data) { 112 struct tfp410_platform_data *pdata = dssdev->data; 113 114 ddata->pd_gpio = pdata->power_down_gpio; 115 i2c_bus_num = pdata->i2c_bus_num; 116 } else { 117 ddata->pd_gpio = -1; 118 i2c_bus_num = -1; 119 } 120 121 if (gpio_is_valid(ddata->pd_gpio)) { 122 r = devm_gpio_request_one(&dssdev->dev, ddata->pd_gpio, 123 GPIOF_OUT_INIT_LOW, "tfp410 pd"); 124 if (r) { 125 dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n", 126 ddata->pd_gpio); 127 return r; 128 } 129 } 130 131 if (i2c_bus_num != -1) { 132 struct i2c_adapter *adapter; 133 134 adapter = i2c_get_adapter(i2c_bus_num); 135 if (!adapter) { 136 dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", 137 i2c_bus_num); 138 return -EINVAL; 139 } 140 141 ddata->i2c_adapter = adapter; 142 } 143 144 dev_set_drvdata(&dssdev->dev, ddata); 145 146 return 0; 147} 148 149static void __exit tfp410_remove(struct omap_dss_device *dssdev) 150{ 151 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 152 153 mutex_lock(&ddata->lock); 154 155 if (ddata->i2c_adapter) 156 i2c_put_adapter(ddata->i2c_adapter); 157 158 dev_set_drvdata(&dssdev->dev, NULL); 159 160 mutex_unlock(&ddata->lock); 161} 162 163static int tfp410_enable(struct omap_dss_device *dssdev) 164{ 165 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 166 int r; 167 168 mutex_lock(&ddata->lock); 169 170 r = tfp410_power_on(dssdev); 171 if (r == 0) 172 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 173 174 mutex_unlock(&ddata->lock); 175 176 return r; 177} 178 179static void tfp410_disable(struct omap_dss_device *dssdev) 180{ 181 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 182 183 mutex_lock(&ddata->lock); 184 185 tfp410_power_off(dssdev); 186 187 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 188 189 mutex_unlock(&ddata->lock); 190} 191 192static int tfp410_suspend(struct omap_dss_device *dssdev) 193{ 194 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 195 196 mutex_lock(&ddata->lock); 197 198 tfp410_power_off(dssdev); 199 200 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; 201 202 mutex_unlock(&ddata->lock); 203 204 return 0; 205} 206 207static int tfp410_resume(struct omap_dss_device *dssdev) 208{ 209 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 210 int r; 211 212 mutex_lock(&ddata->lock); 213 214 r = tfp410_power_on(dssdev); 215 if (r == 0) 216 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 217 218 mutex_unlock(&ddata->lock); 219 220 return r; 221} 222 223static void tfp410_set_timings(struct omap_dss_device *dssdev, 224 struct omap_video_timings *timings) 225{ 226 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 227 228 mutex_lock(&ddata->lock); 229 omapdss_dpi_set_timings(dssdev, timings); 230 dssdev->panel.timings = *timings; 231 mutex_unlock(&ddata->lock); 232} 233 234static void tfp410_get_timings(struct omap_dss_device *dssdev, 235 struct omap_video_timings *timings) 236{ 237 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 238 239 mutex_lock(&ddata->lock); 240 *timings = dssdev->panel.timings; 241 mutex_unlock(&ddata->lock); 242} 243 244static int tfp410_check_timings(struct omap_dss_device *dssdev, 245 struct omap_video_timings *timings) 246{ 247 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 248 int r; 249 250 mutex_lock(&ddata->lock); 251 r = dpi_check_timings(dssdev, timings); 252 mutex_unlock(&ddata->lock); 253 254 return r; 255} 256 257 258static int tfp410_ddc_read(struct i2c_adapter *adapter, 259 unsigned char *buf, u16 count, u8 offset) 260{ 261 int r, retries; 262 263 for (retries = 3; retries > 0; retries--) { 264 struct i2c_msg msgs[] = { 265 { 266 .addr = DDC_ADDR, 267 .flags = 0, 268 .len = 1, 269 .buf = &offset, 270 }, { 271 .addr = DDC_ADDR, 272 .flags = I2C_M_RD, 273 .len = count, 274 .buf = buf, 275 } 276 }; 277 278 r = i2c_transfer(adapter, msgs, 2); 279 if (r == 2) 280 return 0; 281 282 if (r != -EAGAIN) 283 break; 284 } 285 286 return r < 0 ? r : -EIO; 287} 288 289static int tfp410_read_edid(struct omap_dss_device *dssdev, 290 u8 *edid, int len) 291{ 292 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 293 int r, l, bytes_read; 294 295 mutex_lock(&ddata->lock); 296 297 if (!ddata->i2c_adapter) { 298 r = -ENODEV; 299 goto err; 300 } 301 302 l = min(EDID_LENGTH, len); 303 r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0); 304 if (r) 305 goto err; 306 307 bytes_read = l; 308 309 /* if there are extensions, read second block */ 310 if (len > EDID_LENGTH && edid[0x7e] > 0) { 311 l = min(EDID_LENGTH, len - EDID_LENGTH); 312 313 r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, 314 l, EDID_LENGTH); 315 if (r) 316 goto err; 317 318 bytes_read += l; 319 } 320 321 mutex_unlock(&ddata->lock); 322 323 return bytes_read; 324 325err: 326 mutex_unlock(&ddata->lock); 327 return r; 328} 329 330static bool tfp410_detect(struct omap_dss_device *dssdev) 331{ 332 struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); 333 unsigned char out; 334 int r; 335 336 mutex_lock(&ddata->lock); 337 338 if (!ddata->i2c_adapter) 339 goto out; 340 341 r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0); 342 343 mutex_unlock(&ddata->lock); 344 345 return r == 0; 346 347out: 348 mutex_unlock(&ddata->lock); 349 return true; 350} 351 352static struct omap_dss_driver tfp410_driver = { 353 .probe = tfp410_probe, 354 .remove = __exit_p(tfp410_remove), 355 356 .enable = tfp410_enable, 357 .disable = tfp410_disable, 358 .suspend = tfp410_suspend, 359 .resume = tfp410_resume, 360 361 .set_timings = tfp410_set_timings, 362 .get_timings = tfp410_get_timings, 363 .check_timings = tfp410_check_timings, 364 365 .read_edid = tfp410_read_edid, 366 .detect = tfp410_detect, 367 368 .driver = { 369 .name = "tfp410", 370 .owner = THIS_MODULE, 371 }, 372}; 373 374static int __init tfp410_init(void) 375{ 376 return omap_dss_register_driver(&tfp410_driver); 377} 378 379static void __exit tfp410_exit(void) 380{ 381 omap_dss_unregister_driver(&tfp410_driver); 382} 383 384module_init(tfp410_init); 385module_exit(tfp410_exit); 386MODULE_LICENSE("GPL");