cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

connector-dvi.c (7277B)


      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
     18static 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
     39struct 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
     50static 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
     61static 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
     72static 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
     95static 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
    108static 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
    120static 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
    128static 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
    137static 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
    168static 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
    199static 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
    213static 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
    230static 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		of_node_put(adapter_node);
    250		if (adapter == NULL) {
    251			dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
    252			omap_dss_put_device(ddata->in);
    253			return -EPROBE_DEFER;
    254		}
    255
    256		ddata->i2c_adapter = adapter;
    257	}
    258
    259	return 0;
    260}
    261
    262static int dvic_probe(struct platform_device *pdev)
    263{
    264	struct panel_drv_data *ddata;
    265	struct omap_dss_device *dssdev;
    266	int r;
    267
    268	if (!pdev->dev.of_node)
    269		return -ENODEV;
    270
    271	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
    272	if (!ddata)
    273		return -ENOMEM;
    274
    275	platform_set_drvdata(pdev, ddata);
    276
    277	r = dvic_probe_of(pdev);
    278	if (r)
    279		return r;
    280
    281	ddata->timings = dvic_default_timings;
    282
    283	dssdev = &ddata->dssdev;
    284	dssdev->driver = &dvic_driver;
    285	dssdev->dev = &pdev->dev;
    286	dssdev->type = OMAP_DISPLAY_TYPE_DVI;
    287	dssdev->owner = THIS_MODULE;
    288	dssdev->panel.timings = dvic_default_timings;
    289
    290	r = omapdss_register_display(dssdev);
    291	if (r) {
    292		dev_err(&pdev->dev, "Failed to register panel\n");
    293		goto err_reg;
    294	}
    295
    296	return 0;
    297
    298err_reg:
    299	omap_dss_put_device(ddata->in);
    300
    301	i2c_put_adapter(ddata->i2c_adapter);
    302
    303	return r;
    304}
    305
    306static int __exit dvic_remove(struct platform_device *pdev)
    307{
    308	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
    309	struct omap_dss_device *dssdev = &ddata->dssdev;
    310	struct omap_dss_device *in = ddata->in;
    311
    312	omapdss_unregister_display(&ddata->dssdev);
    313
    314	dvic_disable(dssdev);
    315	dvic_disconnect(dssdev);
    316
    317	omap_dss_put_device(in);
    318
    319	i2c_put_adapter(ddata->i2c_adapter);
    320
    321	return 0;
    322}
    323
    324static const struct of_device_id dvic_of_match[] = {
    325	{ .compatible = "omapdss,dvi-connector", },
    326	{},
    327};
    328
    329MODULE_DEVICE_TABLE(of, dvic_of_match);
    330
    331static struct platform_driver dvi_connector_driver = {
    332	.probe	= dvic_probe,
    333	.remove	= __exit_p(dvic_remove),
    334	.driver	= {
    335		.name	= "connector-dvi",
    336		.of_match_table = dvic_of_match,
    337		.suppress_bind_attrs = true,
    338	},
    339};
    340
    341module_platform_driver(dvi_connector_driver);
    342
    343MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
    344MODULE_DESCRIPTION("Generic DVI Connector driver");
    345MODULE_LICENSE("GPL");