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