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