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