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

panel-lgphilips-lb035q02.c (8343B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LG.Philips LB035Q02 LCD Panel driver
      4 *
      5 * Copyright (C) 2013 Texas Instruments
      6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
      7 * Based on a driver by: Steve Sakoman <steve@sakoman.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/delay.h>
     12#include <linux/spi/spi.h>
     13#include <linux/mutex.h>
     14#include <linux/gpio.h>
     15
     16#include <video/omapfb_dss.h>
     17
     18static const struct omap_video_timings lb035q02_timings = {
     19	.x_res = 320,
     20	.y_res = 240,
     21
     22	.pixelclock	= 6500000,
     23
     24	.hsw		= 2,
     25	.hfp		= 20,
     26	.hbp		= 68,
     27
     28	.vsw		= 2,
     29	.vfp		= 4,
     30	.vbp		= 18,
     31
     32	.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
     33	.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
     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 spi_device *spi;
     44
     45	int data_lines;
     46
     47	struct omap_video_timings videomode;
     48
     49	/* used for non-DT boot, to be removed */
     50	int backlight_gpio;
     51
     52	struct gpio_desc *enable_gpio;
     53};
     54
     55#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
     56
     57static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
     58{
     59	struct spi_message msg;
     60	struct spi_transfer index_xfer = {
     61		.len		= 3,
     62		.cs_change	= 1,
     63	};
     64	struct spi_transfer value_xfer = {
     65		.len		= 3,
     66	};
     67	u8	buffer[16];
     68
     69	spi_message_init(&msg);
     70
     71	/* register index */
     72	buffer[0] = 0x70;
     73	buffer[1] = 0x00;
     74	buffer[2] = reg & 0x7f;
     75	index_xfer.tx_buf = buffer;
     76	spi_message_add_tail(&index_xfer, &msg);
     77
     78	/* register value */
     79	buffer[4] = 0x72;
     80	buffer[5] = val >> 8;
     81	buffer[6] = val;
     82	value_xfer.tx_buf = buffer + 4;
     83	spi_message_add_tail(&value_xfer, &msg);
     84
     85	return spi_sync(spi, &msg);
     86}
     87
     88static void init_lb035q02_panel(struct spi_device *spi)
     89{
     90	/* Init sequence from page 28 of the lb035q02 spec */
     91	lb035q02_write_reg(spi, 0x01, 0x6300);
     92	lb035q02_write_reg(spi, 0x02, 0x0200);
     93	lb035q02_write_reg(spi, 0x03, 0x0177);
     94	lb035q02_write_reg(spi, 0x04, 0x04c7);
     95	lb035q02_write_reg(spi, 0x05, 0xffc0);
     96	lb035q02_write_reg(spi, 0x06, 0xe806);
     97	lb035q02_write_reg(spi, 0x0a, 0x4008);
     98	lb035q02_write_reg(spi, 0x0b, 0x0000);
     99	lb035q02_write_reg(spi, 0x0d, 0x0030);
    100	lb035q02_write_reg(spi, 0x0e, 0x2800);
    101	lb035q02_write_reg(spi, 0x0f, 0x0000);
    102	lb035q02_write_reg(spi, 0x16, 0x9f80);
    103	lb035q02_write_reg(spi, 0x17, 0x0a0f);
    104	lb035q02_write_reg(spi, 0x1e, 0x00c1);
    105	lb035q02_write_reg(spi, 0x30, 0x0300);
    106	lb035q02_write_reg(spi, 0x31, 0x0007);
    107	lb035q02_write_reg(spi, 0x32, 0x0000);
    108	lb035q02_write_reg(spi, 0x33, 0x0000);
    109	lb035q02_write_reg(spi, 0x34, 0x0707);
    110	lb035q02_write_reg(spi, 0x35, 0x0004);
    111	lb035q02_write_reg(spi, 0x36, 0x0302);
    112	lb035q02_write_reg(spi, 0x37, 0x0202);
    113	lb035q02_write_reg(spi, 0x3a, 0x0a0d);
    114	lb035q02_write_reg(spi, 0x3b, 0x0806);
    115}
    116
    117static int lb035q02_connect(struct omap_dss_device *dssdev)
    118{
    119	struct panel_drv_data *ddata = to_panel_data(dssdev);
    120	struct omap_dss_device *in = ddata->in;
    121	int r;
    122
    123	if (omapdss_device_is_connected(dssdev))
    124		return 0;
    125
    126	r = in->ops.dpi->connect(in, dssdev);
    127	if (r)
    128		return r;
    129
    130	init_lb035q02_panel(ddata->spi);
    131
    132	return 0;
    133}
    134
    135static void lb035q02_disconnect(struct omap_dss_device *dssdev)
    136{
    137	struct panel_drv_data *ddata = to_panel_data(dssdev);
    138	struct omap_dss_device *in = ddata->in;
    139
    140	if (!omapdss_device_is_connected(dssdev))
    141		return;
    142
    143	in->ops.dpi->disconnect(in, dssdev);
    144}
    145
    146static int lb035q02_enable(struct omap_dss_device *dssdev)
    147{
    148	struct panel_drv_data *ddata = to_panel_data(dssdev);
    149	struct omap_dss_device *in = ddata->in;
    150	int r;
    151
    152	if (!omapdss_device_is_connected(dssdev))
    153		return -ENODEV;
    154
    155	if (omapdss_device_is_enabled(dssdev))
    156		return 0;
    157
    158	if (ddata->data_lines)
    159		in->ops.dpi->set_data_lines(in, ddata->data_lines);
    160	in->ops.dpi->set_timings(in, &ddata->videomode);
    161
    162	r = in->ops.dpi->enable(in);
    163	if (r)
    164		return r;
    165
    166	if (ddata->enable_gpio)
    167		gpiod_set_value_cansleep(ddata->enable_gpio, 1);
    168
    169	if (gpio_is_valid(ddata->backlight_gpio))
    170		gpio_set_value_cansleep(ddata->backlight_gpio, 1);
    171
    172	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
    173
    174	return 0;
    175}
    176
    177static void lb035q02_disable(struct omap_dss_device *dssdev)
    178{
    179	struct panel_drv_data *ddata = to_panel_data(dssdev);
    180	struct omap_dss_device *in = ddata->in;
    181
    182	if (!omapdss_device_is_enabled(dssdev))
    183		return;
    184
    185	if (ddata->enable_gpio)
    186		gpiod_set_value_cansleep(ddata->enable_gpio, 0);
    187
    188	if (gpio_is_valid(ddata->backlight_gpio))
    189		gpio_set_value_cansleep(ddata->backlight_gpio, 0);
    190
    191	in->ops.dpi->disable(in);
    192
    193	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
    194}
    195
    196static void lb035q02_set_timings(struct omap_dss_device *dssdev,
    197		struct omap_video_timings *timings)
    198{
    199	struct panel_drv_data *ddata = to_panel_data(dssdev);
    200	struct omap_dss_device *in = ddata->in;
    201
    202	ddata->videomode = *timings;
    203	dssdev->panel.timings = *timings;
    204
    205	in->ops.dpi->set_timings(in, timings);
    206}
    207
    208static void lb035q02_get_timings(struct omap_dss_device *dssdev,
    209		struct omap_video_timings *timings)
    210{
    211	struct panel_drv_data *ddata = to_panel_data(dssdev);
    212
    213	*timings = ddata->videomode;
    214}
    215
    216static int lb035q02_check_timings(struct omap_dss_device *dssdev,
    217		struct omap_video_timings *timings)
    218{
    219	struct panel_drv_data *ddata = to_panel_data(dssdev);
    220	struct omap_dss_device *in = ddata->in;
    221
    222	return in->ops.dpi->check_timings(in, timings);
    223}
    224
    225static struct omap_dss_driver lb035q02_ops = {
    226	.connect	= lb035q02_connect,
    227	.disconnect	= lb035q02_disconnect,
    228
    229	.enable		= lb035q02_enable,
    230	.disable	= lb035q02_disable,
    231
    232	.set_timings	= lb035q02_set_timings,
    233	.get_timings	= lb035q02_get_timings,
    234	.check_timings	= lb035q02_check_timings,
    235
    236	.get_resolution	= omapdss_default_get_resolution,
    237};
    238
    239static int lb035q02_probe_of(struct spi_device *spi)
    240{
    241	struct device_node *node = spi->dev.of_node;
    242	struct panel_drv_data *ddata = spi_get_drvdata(spi);
    243	struct omap_dss_device *in;
    244	struct gpio_desc *gpio;
    245
    246	gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
    247	if (IS_ERR(gpio))
    248		return dev_err_probe(&spi->dev, PTR_ERR(gpio),
    249				     "failed to parse enable gpio\n");
    250
    251	ddata->enable_gpio = gpio;
    252
    253	ddata->backlight_gpio = -ENOENT;
    254
    255	in = omapdss_of_find_source_for_first_ep(node);
    256	if (IS_ERR(in)) {
    257		dev_err(&spi->dev, "failed to find video source\n");
    258		return PTR_ERR(in);
    259	}
    260
    261	ddata->in = in;
    262
    263	return 0;
    264}
    265
    266static int lb035q02_panel_spi_probe(struct spi_device *spi)
    267{
    268	struct panel_drv_data *ddata;
    269	struct omap_dss_device *dssdev;
    270	int r;
    271
    272	if (!spi->dev.of_node)
    273		return -ENODEV;
    274
    275	ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
    276	if (ddata == NULL)
    277		return -ENOMEM;
    278
    279	spi_set_drvdata(spi, ddata);
    280
    281	ddata->spi = spi;
    282
    283	r = lb035q02_probe_of(spi);
    284	if (r)
    285		return r;
    286
    287	if (gpio_is_valid(ddata->backlight_gpio)) {
    288		r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
    289				GPIOF_OUT_INIT_LOW, "panel backlight");
    290		if (r)
    291			goto err_gpio;
    292	}
    293
    294	ddata->videomode = lb035q02_timings;
    295
    296	dssdev = &ddata->dssdev;
    297	dssdev->dev = &spi->dev;
    298	dssdev->driver = &lb035q02_ops;
    299	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
    300	dssdev->owner = THIS_MODULE;
    301	dssdev->panel.timings = ddata->videomode;
    302	dssdev->phy.dpi.data_lines = ddata->data_lines;
    303
    304	r = omapdss_register_display(dssdev);
    305	if (r) {
    306		dev_err(&spi->dev, "Failed to register panel\n");
    307		goto err_reg;
    308	}
    309
    310	return 0;
    311
    312err_reg:
    313err_gpio:
    314	omap_dss_put_device(ddata->in);
    315	return r;
    316}
    317
    318static void lb035q02_panel_spi_remove(struct spi_device *spi)
    319{
    320	struct panel_drv_data *ddata = spi_get_drvdata(spi);
    321	struct omap_dss_device *dssdev = &ddata->dssdev;
    322	struct omap_dss_device *in = ddata->in;
    323
    324	omapdss_unregister_display(dssdev);
    325
    326	lb035q02_disable(dssdev);
    327	lb035q02_disconnect(dssdev);
    328
    329	omap_dss_put_device(in);
    330}
    331
    332static const struct of_device_id lb035q02_of_match[] = {
    333	{ .compatible = "omapdss,lgphilips,lb035q02", },
    334	{},
    335};
    336
    337MODULE_DEVICE_TABLE(of, lb035q02_of_match);
    338
    339static struct spi_driver lb035q02_spi_driver = {
    340	.probe		= lb035q02_panel_spi_probe,
    341	.remove		= lb035q02_panel_spi_remove,
    342	.driver		= {
    343		.name	= "panel_lgphilips_lb035q02",
    344		.of_match_table = lb035q02_of_match,
    345		.suppress_bind_attrs = true,
    346	},
    347};
    348
    349module_spi_driver(lb035q02_spi_driver);
    350
    351MODULE_ALIAS("spi:lgphilips,lb035q02");
    352MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
    353MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
    354MODULE_LICENSE("GPL");