1 /* 2 * Generic DVI Connector driver 3 * 4 * Copyright (C) 2013 Texas Instruments 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 12 #include <linux/i2c.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 17 #include <drm/drm_edid.h> 18 19 #include <video/omapdss.h> 20 #include <video/omap-panel-data.h> 21 22 static const struct omap_video_timings dvic_default_timings = { 23 .x_res = 640, 24 .y_res = 480, 25 26 .pixelclock = 23500000, 27 28 .hfp = 48, 29 .hsw = 32, 30 .hbp = 80, 31 32 .vfp = 3, 33 .vsw = 4, 34 .vbp = 7, 35 36 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, 37 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, 38 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 39 .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 40 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, 41 }; 42 43 struct panel_drv_data { 44 struct omap_dss_device dssdev; 45 struct omap_dss_device *in; 46 47 struct omap_video_timings timings; 48 49 struct i2c_adapter *i2c_adapter; 50 }; 51 52 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 53 54 static int dvic_connect(struct omap_dss_device *dssdev) 55 { 56 struct panel_drv_data *ddata = to_panel_data(dssdev); 57 struct omap_dss_device *in = ddata->in; 58 int r; 59 60 if (omapdss_device_is_connected(dssdev)) 61 return 0; 62 63 r = in->ops.dvi->connect(in, dssdev); 64 if (r) 65 return r; 66 67 return 0; 68 } 69 70 static void dvic_disconnect(struct omap_dss_device *dssdev) 71 { 72 struct panel_drv_data *ddata = to_panel_data(dssdev); 73 struct omap_dss_device *in = ddata->in; 74 75 if (!omapdss_device_is_connected(dssdev)) 76 return; 77 78 in->ops.dvi->disconnect(in, dssdev); 79 } 80 81 static int dvic_enable(struct omap_dss_device *dssdev) 82 { 83 struct panel_drv_data *ddata = to_panel_data(dssdev); 84 struct omap_dss_device *in = ddata->in; 85 int r; 86 87 if (!omapdss_device_is_connected(dssdev)) 88 return -ENODEV; 89 90 if (omapdss_device_is_enabled(dssdev)) 91 return 0; 92 93 in->ops.dvi->set_timings(in, &ddata->timings); 94 95 r = in->ops.dvi->enable(in); 96 if (r) 97 return r; 98 99 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 100 101 return 0; 102 } 103 104 static void dvic_disable(struct omap_dss_device *dssdev) 105 { 106 struct panel_drv_data *ddata = to_panel_data(dssdev); 107 struct omap_dss_device *in = ddata->in; 108 109 if (!omapdss_device_is_enabled(dssdev)) 110 return; 111 112 in->ops.dvi->disable(in); 113 114 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 115 } 116 117 static void dvic_set_timings(struct omap_dss_device *dssdev, 118 struct omap_video_timings *timings) 119 { 120 struct panel_drv_data *ddata = to_panel_data(dssdev); 121 struct omap_dss_device *in = ddata->in; 122 123 ddata->timings = *timings; 124 dssdev->panel.timings = *timings; 125 126 in->ops.dvi->set_timings(in, timings); 127 } 128 129 static void dvic_get_timings(struct omap_dss_device *dssdev, 130 struct omap_video_timings *timings) 131 { 132 struct panel_drv_data *ddata = to_panel_data(dssdev); 133 134 *timings = ddata->timings; 135 } 136 137 static int dvic_check_timings(struct omap_dss_device *dssdev, 138 struct omap_video_timings *timings) 139 { 140 struct panel_drv_data *ddata = to_panel_data(dssdev); 141 struct omap_dss_device *in = ddata->in; 142 143 return in->ops.dvi->check_timings(in, timings); 144 } 145 146 static int dvic_ddc_read(struct i2c_adapter *adapter, 147 unsigned char *buf, u16 count, u8 offset) 148 { 149 int r, retries; 150 151 for (retries = 3; retries > 0; retries--) { 152 struct i2c_msg msgs[] = { 153 { 154 .addr = DDC_ADDR, 155 .flags = 0, 156 .len = 1, 157 .buf = &offset, 158 }, { 159 .addr = DDC_ADDR, 160 .flags = I2C_M_RD, 161 .len = count, 162 .buf = buf, 163 } 164 }; 165 166 r = i2c_transfer(adapter, msgs, 2); 167 if (r == 2) 168 return 0; 169 170 if (r != -EAGAIN) 171 break; 172 } 173 174 return r < 0 ? r : -EIO; 175 } 176 177 static int dvic_read_edid(struct omap_dss_device *dssdev, 178 u8 *edid, int len) 179 { 180 struct panel_drv_data *ddata = to_panel_data(dssdev); 181 int r, l, bytes_read; 182 183 if (!ddata->i2c_adapter) 184 return -ENODEV; 185 186 l = min(EDID_LENGTH, len); 187 r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0); 188 if (r) 189 return r; 190 191 bytes_read = l; 192 193 /* if there are extensions, read second block */ 194 if (len > EDID_LENGTH && edid[0x7e] > 0) { 195 l = min(EDID_LENGTH, len - EDID_LENGTH); 196 197 r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, 198 l, EDID_LENGTH); 199 if (r) 200 return r; 201 202 bytes_read += l; 203 } 204 205 return bytes_read; 206 } 207 208 static bool dvic_detect(struct omap_dss_device *dssdev) 209 { 210 struct panel_drv_data *ddata = to_panel_data(dssdev); 211 unsigned char out; 212 int r; 213 214 if (!ddata->i2c_adapter) 215 return true; 216 217 r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0); 218 219 return r == 0; 220 } 221 222 static struct omap_dss_driver dvic_driver = { 223 .connect = dvic_connect, 224 .disconnect = dvic_disconnect, 225 226 .enable = dvic_enable, 227 .disable = dvic_disable, 228 229 .set_timings = dvic_set_timings, 230 .get_timings = dvic_get_timings, 231 .check_timings = dvic_check_timings, 232 233 .get_resolution = omapdss_default_get_resolution, 234 235 .read_edid = dvic_read_edid, 236 .detect = dvic_detect, 237 }; 238 239 static int dvic_probe_pdata(struct platform_device *pdev) 240 { 241 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 242 struct connector_dvi_platform_data *pdata; 243 struct omap_dss_device *in, *dssdev; 244 int i2c_bus_num; 245 246 pdata = dev_get_platdata(&pdev->dev); 247 i2c_bus_num = pdata->i2c_bus_num; 248 249 if (i2c_bus_num != -1) { 250 struct i2c_adapter *adapter; 251 252 adapter = i2c_get_adapter(i2c_bus_num); 253 if (!adapter) { 254 dev_err(&pdev->dev, 255 "Failed to get I2C adapter, bus %d\n", 256 i2c_bus_num); 257 return -EPROBE_DEFER; 258 } 259 260 ddata->i2c_adapter = adapter; 261 } 262 263 in = omap_dss_find_output(pdata->source); 264 if (in == NULL) { 265 i2c_put_adapter(ddata->i2c_adapter); 266 267 dev_err(&pdev->dev, "Failed to find video source\n"); 268 return -EPROBE_DEFER; 269 } 270 271 ddata->in = in; 272 273 dssdev = &ddata->dssdev; 274 dssdev->name = pdata->name; 275 276 return 0; 277 } 278 279 static int dvic_probe_of(struct platform_device *pdev) 280 { 281 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 282 struct device_node *node = pdev->dev.of_node; 283 struct omap_dss_device *in; 284 struct device_node *adapter_node; 285 struct i2c_adapter *adapter; 286 287 in = omapdss_of_find_source_for_first_ep(node); 288 if (IS_ERR(in)) { 289 dev_err(&pdev->dev, "failed to find video source\n"); 290 return PTR_ERR(in); 291 } 292 293 ddata->in = in; 294 295 adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0); 296 if (adapter_node) { 297 adapter = of_get_i2c_adapter_by_node(adapter_node); 298 if (adapter == NULL) { 299 dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n"); 300 omap_dss_put_device(ddata->in); 301 return -EPROBE_DEFER; 302 } 303 304 ddata->i2c_adapter = adapter; 305 } 306 307 return 0; 308 } 309 310 static int dvic_probe(struct platform_device *pdev) 311 { 312 struct panel_drv_data *ddata; 313 struct omap_dss_device *dssdev; 314 int r; 315 316 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 317 if (!ddata) 318 return -ENOMEM; 319 320 platform_set_drvdata(pdev, ddata); 321 322 if (dev_get_platdata(&pdev->dev)) { 323 r = dvic_probe_pdata(pdev); 324 if (r) 325 return r; 326 } else if (pdev->dev.of_node) { 327 r = dvic_probe_of(pdev); 328 if (r) 329 return r; 330 } else { 331 return -ENODEV; 332 } 333 334 ddata->timings = dvic_default_timings; 335 336 dssdev = &ddata->dssdev; 337 dssdev->driver = &dvic_driver; 338 dssdev->dev = &pdev->dev; 339 dssdev->type = OMAP_DISPLAY_TYPE_DVI; 340 dssdev->owner = THIS_MODULE; 341 dssdev->panel.timings = dvic_default_timings; 342 343 r = omapdss_register_display(dssdev); 344 if (r) { 345 dev_err(&pdev->dev, "Failed to register panel\n"); 346 goto err_reg; 347 } 348 349 return 0; 350 351 err_reg: 352 omap_dss_put_device(ddata->in); 353 354 i2c_put_adapter(ddata->i2c_adapter); 355 356 return r; 357 } 358 359 static int __exit dvic_remove(struct platform_device *pdev) 360 { 361 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 362 struct omap_dss_device *dssdev = &ddata->dssdev; 363 struct omap_dss_device *in = ddata->in; 364 365 omapdss_unregister_display(&ddata->dssdev); 366 367 dvic_disable(dssdev); 368 dvic_disconnect(dssdev); 369 370 omap_dss_put_device(in); 371 372 i2c_put_adapter(ddata->i2c_adapter); 373 374 return 0; 375 } 376 377 static const struct of_device_id dvic_of_match[] = { 378 { .compatible = "omapdss,dvi-connector", }, 379 {}, 380 }; 381 382 MODULE_DEVICE_TABLE(of, dvic_of_match); 383 384 static struct platform_driver dvi_connector_driver = { 385 .probe = dvic_probe, 386 .remove = __exit_p(dvic_remove), 387 .driver = { 388 .name = "connector-dvi", 389 .of_match_table = dvic_of_match, 390 .suppress_bind_attrs = true, 391 }, 392 }; 393 394 module_platform_driver(dvi_connector_driver); 395 396 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 397 MODULE_DESCRIPTION("Generic DVI Connector driver"); 398 MODULE_LICENSE("GPL"); 399