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

encoder-tpd12s015.c (7242B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * TPD12S015 HDMI ESD protection & level shifter chip driver
      4 *
      5 * Copyright (C) 2013 Texas Instruments
      6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
      7 */
      8
      9#include <linux/completion.h>
     10#include <linux/delay.h>
     11#include <linux/module.h>
     12#include <linux/mod_devicetable.h>
     13#include <linux/slab.h>
     14#include <linux/platform_device.h>
     15#include <linux/gpio/consumer.h>
     16
     17#include <video/omapfb_dss.h>
     18
     19struct panel_drv_data {
     20	struct omap_dss_device dssdev;
     21	struct omap_dss_device *in;
     22
     23	struct gpio_desc *ct_cp_hpd_gpio;
     24	struct gpio_desc *ls_oe_gpio;
     25	struct gpio_desc *hpd_gpio;
     26
     27	struct omap_video_timings timings;
     28};
     29
     30#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
     31
     32static int tpd_connect(struct omap_dss_device *dssdev,
     33		struct omap_dss_device *dst)
     34{
     35	struct panel_drv_data *ddata = to_panel_data(dssdev);
     36	struct omap_dss_device *in = ddata->in;
     37	int r;
     38
     39	r = in->ops.hdmi->connect(in, dssdev);
     40	if (r)
     41		return r;
     42
     43	dst->src = dssdev;
     44	dssdev->dst = dst;
     45
     46	if (ddata->ct_cp_hpd_gpio) {
     47		gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
     48		/* DC-DC converter needs at max 300us to get to 90% of 5V */
     49		udelay(300);
     50	}
     51
     52	return 0;
     53}
     54
     55static void tpd_disconnect(struct omap_dss_device *dssdev,
     56		struct omap_dss_device *dst)
     57{
     58	struct panel_drv_data *ddata = to_panel_data(dssdev);
     59	struct omap_dss_device *in = ddata->in;
     60
     61	WARN_ON(dst != dssdev->dst);
     62
     63	if (dst != dssdev->dst)
     64		return;
     65
     66	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
     67
     68	dst->src = NULL;
     69	dssdev->dst = NULL;
     70
     71	in->ops.hdmi->disconnect(in, &ddata->dssdev);
     72}
     73
     74static int tpd_enable(struct omap_dss_device *dssdev)
     75{
     76	struct panel_drv_data *ddata = to_panel_data(dssdev);
     77	struct omap_dss_device *in = ddata->in;
     78	int r;
     79
     80	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
     81		return 0;
     82
     83	in->ops.hdmi->set_timings(in, &ddata->timings);
     84
     85	r = in->ops.hdmi->enable(in);
     86	if (r)
     87		return r;
     88
     89	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
     90
     91	return r;
     92}
     93
     94static void tpd_disable(struct omap_dss_device *dssdev)
     95{
     96	struct panel_drv_data *ddata = to_panel_data(dssdev);
     97	struct omap_dss_device *in = ddata->in;
     98
     99	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
    100		return;
    101
    102	in->ops.hdmi->disable(in);
    103
    104	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
    105}
    106
    107static void tpd_set_timings(struct omap_dss_device *dssdev,
    108		struct omap_video_timings *timings)
    109{
    110	struct panel_drv_data *ddata = to_panel_data(dssdev);
    111	struct omap_dss_device *in = ddata->in;
    112
    113	ddata->timings = *timings;
    114	dssdev->panel.timings = *timings;
    115
    116	in->ops.hdmi->set_timings(in, timings);
    117}
    118
    119static void tpd_get_timings(struct omap_dss_device *dssdev,
    120		struct omap_video_timings *timings)
    121{
    122	struct panel_drv_data *ddata = to_panel_data(dssdev);
    123
    124	*timings = ddata->timings;
    125}
    126
    127static int tpd_check_timings(struct omap_dss_device *dssdev,
    128		struct omap_video_timings *timings)
    129{
    130	struct panel_drv_data *ddata = to_panel_data(dssdev);
    131	struct omap_dss_device *in = ddata->in;
    132	int r;
    133
    134	r = in->ops.hdmi->check_timings(in, timings);
    135
    136	return r;
    137}
    138
    139static int tpd_read_edid(struct omap_dss_device *dssdev,
    140		u8 *edid, int len)
    141{
    142	struct panel_drv_data *ddata = to_panel_data(dssdev);
    143	struct omap_dss_device *in = ddata->in;
    144	int r;
    145
    146	if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
    147		return -ENODEV;
    148
    149	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
    150
    151	r = in->ops.hdmi->read_edid(in, edid, len);
    152
    153	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
    154
    155	return r;
    156}
    157
    158static bool tpd_detect(struct omap_dss_device *dssdev)
    159{
    160	struct panel_drv_data *ddata = to_panel_data(dssdev);
    161
    162	return gpiod_get_value_cansleep(ddata->hpd_gpio);
    163}
    164
    165static int tpd_set_infoframe(struct omap_dss_device *dssdev,
    166		const struct hdmi_avi_infoframe *avi)
    167{
    168	struct panel_drv_data *ddata = to_panel_data(dssdev);
    169	struct omap_dss_device *in = ddata->in;
    170
    171	return in->ops.hdmi->set_infoframe(in, avi);
    172}
    173
    174static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
    175		bool hdmi_mode)
    176{
    177	struct panel_drv_data *ddata = to_panel_data(dssdev);
    178	struct omap_dss_device *in = ddata->in;
    179
    180	return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
    181}
    182
    183static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
    184	.connect		= tpd_connect,
    185	.disconnect		= tpd_disconnect,
    186
    187	.enable			= tpd_enable,
    188	.disable		= tpd_disable,
    189
    190	.check_timings		= tpd_check_timings,
    191	.set_timings		= tpd_set_timings,
    192	.get_timings		= tpd_get_timings,
    193
    194	.read_edid		= tpd_read_edid,
    195	.detect			= tpd_detect,
    196	.set_infoframe		= tpd_set_infoframe,
    197	.set_hdmi_mode		= tpd_set_hdmi_mode,
    198};
    199
    200static int tpd_probe_of(struct platform_device *pdev)
    201{
    202	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
    203	struct device_node *node = pdev->dev.of_node;
    204	struct omap_dss_device *in;
    205
    206	in = omapdss_of_find_source_for_first_ep(node);
    207	if (IS_ERR(in)) {
    208		dev_err(&pdev->dev, "failed to find video source\n");
    209		return PTR_ERR(in);
    210	}
    211
    212	ddata->in = in;
    213
    214	return 0;
    215}
    216
    217static int tpd_probe(struct platform_device *pdev)
    218{
    219	struct omap_dss_device *dssdev;
    220	struct panel_drv_data *ddata;
    221	int r;
    222	struct gpio_desc *gpio;
    223
    224	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
    225	if (!ddata)
    226		return -ENOMEM;
    227
    228	platform_set_drvdata(pdev, ddata);
    229
    230	if (pdev->dev.of_node) {
    231		r = tpd_probe_of(pdev);
    232		if (r)
    233			return r;
    234	} else {
    235		return -ENODEV;
    236	}
    237
    238	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
    239		GPIOD_OUT_LOW);
    240	if (IS_ERR(gpio)) {
    241		r = PTR_ERR(gpio);
    242		goto err_gpio;
    243	}
    244
    245	ddata->ct_cp_hpd_gpio = gpio;
    246
    247	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
    248		GPIOD_OUT_LOW);
    249	if (IS_ERR(gpio)) {
    250		r = PTR_ERR(gpio);
    251		goto err_gpio;
    252	}
    253
    254	ddata->ls_oe_gpio = gpio;
    255
    256	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
    257		GPIOD_IN);
    258	if (IS_ERR(gpio)) {
    259		r = PTR_ERR(gpio);
    260		goto err_gpio;
    261	}
    262
    263	ddata->hpd_gpio = gpio;
    264
    265	dssdev = &ddata->dssdev;
    266	dssdev->ops.hdmi = &tpd_hdmi_ops;
    267	dssdev->dev = &pdev->dev;
    268	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
    269	dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
    270	dssdev->owner = THIS_MODULE;
    271	dssdev->port_num = 1;
    272
    273	r = omapdss_register_output(dssdev);
    274	if (r) {
    275		dev_err(&pdev->dev, "Failed to register output\n");
    276		goto err_reg;
    277	}
    278
    279	return 0;
    280err_reg:
    281err_gpio:
    282	omap_dss_put_device(ddata->in);
    283	return r;
    284}
    285
    286static int __exit tpd_remove(struct platform_device *pdev)
    287{
    288	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
    289	struct omap_dss_device *dssdev = &ddata->dssdev;
    290	struct omap_dss_device *in = ddata->in;
    291
    292	omapdss_unregister_output(&ddata->dssdev);
    293
    294	WARN_ON(omapdss_device_is_enabled(dssdev));
    295	if (omapdss_device_is_enabled(dssdev))
    296		tpd_disable(dssdev);
    297
    298	WARN_ON(omapdss_device_is_connected(dssdev));
    299	if (omapdss_device_is_connected(dssdev))
    300		tpd_disconnect(dssdev, dssdev->dst);
    301
    302	omap_dss_put_device(in);
    303
    304	return 0;
    305}
    306
    307static const struct of_device_id tpd_of_match[] = {
    308	{ .compatible = "omapdss,ti,tpd12s015", },
    309	{},
    310};
    311
    312MODULE_DEVICE_TABLE(of, tpd_of_match);
    313
    314static struct platform_driver tpd_driver = {
    315	.probe	= tpd_probe,
    316	.remove	= __exit_p(tpd_remove),
    317	.driver	= {
    318		.name	= "tpd12s015",
    319		.of_match_table = tpd_of_match,
    320		.suppress_bind_attrs = true,
    321	},
    322};
    323
    324module_platform_driver(tpd_driver);
    325
    326MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
    327MODULE_DESCRIPTION("TPD12S015 driver");
    328MODULE_LICENSE("GPL");