brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · f85b7a4 Raw
311 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Innolux/Chimei EJ030NA TFT LCD panel driver4 *5 * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>6 * Copyright (C) 2020, Christophe Branchereau <cbranchereau@gmail.com>7 */8 9#include <linux/delay.h>10#include <linux/device.h>11#include <linux/gpio/consumer.h>12#include <linux/media-bus-format.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/regmap.h>17#include <linux/regulator/consumer.h>18#include <linux/spi/spi.h>19 20#include <drm/drm_modes.h>21#include <drm/drm_panel.h>22 23struct ej030na_info {24	const struct drm_display_mode *display_modes;25	unsigned int num_modes;26	u16 width_mm, height_mm;27	u32 bus_format, bus_flags;28};29 30struct ej030na {31	struct drm_panel panel;32	struct spi_device *spi;33	struct regmap *map;34 35	const struct ej030na_info *panel_info;36 37	struct regulator *supply;38	struct gpio_desc *reset_gpio;39};40 41static inline struct ej030na *to_ej030na(struct drm_panel *panel)42{43	return container_of(panel, struct ej030na, panel);44}45 46static const struct reg_sequence ej030na_init_sequence[] = {47	{ 0x05, 0x1e },48	{ 0x05, 0x5c },49	{ 0x02, 0x14 },50	{ 0x03, 0x40 },51	{ 0x04, 0x07 },52	{ 0x06, 0x12 },53	{ 0x07, 0xd2 },54	{ 0x0c, 0x06 },55	{ 0x0d, 0x40 },56	{ 0x0e, 0x40 },57	{ 0x0f, 0x40 },58	{ 0x10, 0x40 },59	{ 0x11, 0x40 },60	{ 0x2f, 0x40 },61	{ 0x5a, 0x02 },62 63	{ 0x30, 0x07 },64	{ 0x31, 0x57 },65	{ 0x32, 0x53 },66	{ 0x33, 0x77 },67	{ 0x34, 0xb8 },68	{ 0x35, 0xbd },69	{ 0x36, 0xb8 },70	{ 0x37, 0xe7 },71	{ 0x38, 0x04 },72	{ 0x39, 0xff },73 74	{ 0x40, 0x0b },75	{ 0x41, 0xb8 },76	{ 0x42, 0xab },77	{ 0x43, 0xb9 },78	{ 0x44, 0x6a },79	{ 0x45, 0x56 },80	{ 0x46, 0x61 },81	{ 0x47, 0x08 },82	{ 0x48, 0x0f },83	{ 0x49, 0x0f },84};85 86static int ej030na_prepare(struct drm_panel *panel)87{88	struct ej030na *priv = to_ej030na(panel);89	struct device *dev = &priv->spi->dev;90	int err;91 92	err = regulator_enable(priv->supply);93	if (err) {94		dev_err(dev, "Failed to enable power supply: %d\n", err);95		return err;96	}97 98	/* Reset the chip */99	gpiod_set_value_cansleep(priv->reset_gpio, 1);100	usleep_range(50, 150);101	gpiod_set_value_cansleep(priv->reset_gpio, 0);102	usleep_range(50, 150);103 104	err = regmap_multi_reg_write(priv->map, ej030na_init_sequence,105				     ARRAY_SIZE(ej030na_init_sequence));106	if (err) {107		dev_err(dev, "Failed to init registers: %d\n", err);108		goto err_disable_regulator;109	}110 111	return 0;112 113err_disable_regulator:114	regulator_disable(priv->supply);115	return err;116}117 118static int ej030na_unprepare(struct drm_panel *panel)119{120	struct ej030na *priv = to_ej030na(panel);121 122	gpiod_set_value_cansleep(priv->reset_gpio, 1);123	regulator_disable(priv->supply);124 125	return 0;126}127 128static int ej030na_enable(struct drm_panel *panel)129{130	struct ej030na *priv = to_ej030na(panel);131 132	/* standby off */133	regmap_write(priv->map, 0x2b, 0x01);134 135	if (panel->backlight) {136		/* Wait for the picture to be ready before enabling backlight */137		msleep(120);138	}139 140	return 0;141}142 143static int ej030na_disable(struct drm_panel *panel)144{145	struct ej030na *priv = to_ej030na(panel);146 147	/* standby on */148	regmap_write(priv->map, 0x2b, 0x00);149 150	return 0;151}152 153static int ej030na_get_modes(struct drm_panel *panel,154			     struct drm_connector *connector)155{156	struct ej030na *priv = to_ej030na(panel);157	const struct ej030na_info *panel_info = priv->panel_info;158	struct drm_display_mode *mode;159	unsigned int i;160 161	for (i = 0; i < panel_info->num_modes; i++) {162		mode = drm_mode_duplicate(connector->dev,163					  &panel_info->display_modes[i]);164		if (!mode)165			return -ENOMEM;166 167		drm_mode_set_name(mode);168 169		mode->type = DRM_MODE_TYPE_DRIVER;170		if (panel_info->num_modes == 1)171			mode->type |= DRM_MODE_TYPE_PREFERRED;172 173		drm_mode_probed_add(connector, mode);174	}175 176	connector->display_info.bpc = 8;177	connector->display_info.width_mm = panel_info->width_mm;178	connector->display_info.height_mm = panel_info->height_mm;179 180	drm_display_info_set_bus_formats(&connector->display_info,181					 &panel_info->bus_format, 1);182	connector->display_info.bus_flags = panel_info->bus_flags;183 184	return panel_info->num_modes;185}186 187static const struct drm_panel_funcs ej030na_funcs = {188	.prepare	= ej030na_prepare,189	.unprepare	= ej030na_unprepare,190	.enable		= ej030na_enable,191	.disable	= ej030na_disable,192	.get_modes	= ej030na_get_modes,193};194 195static const struct regmap_config ej030na_regmap_config = {196	.reg_bits = 8,197	.val_bits = 8,198	.max_register = 0x5a,199};200 201static int ej030na_probe(struct spi_device *spi)202{203	struct device *dev = &spi->dev;204	struct ej030na *priv;205	int err;206 207	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);208	if (!priv)209		return -ENOMEM;210 211	priv->spi = spi;212	spi_set_drvdata(spi, priv);213 214	priv->map = devm_regmap_init_spi(spi, &ej030na_regmap_config);215	if (IS_ERR(priv->map)) {216		dev_err(dev, "Unable to init regmap\n");217		return PTR_ERR(priv->map);218	}219 220	priv->panel_info = of_device_get_match_data(dev);221	if (!priv->panel_info)222		return -EINVAL;223 224	priv->supply = devm_regulator_get(dev, "power");225	if (IS_ERR(priv->supply))226		return dev_err_probe(dev, PTR_ERR(priv->supply),227				     "Failed to get power supply\n");228 229	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);230	if (IS_ERR(priv->reset_gpio))231		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),232				     "Failed to get reset GPIO\n");233 234	drm_panel_init(&priv->panel, dev, &ej030na_funcs,235		       DRM_MODE_CONNECTOR_DPI);236 237	err = drm_panel_of_backlight(&priv->panel);238	if (err)239		return err;240 241	drm_panel_add(&priv->panel);242 243	return 0;244}245 246static void ej030na_remove(struct spi_device *spi)247{248	struct ej030na *priv = spi_get_drvdata(spi);249 250	drm_panel_remove(&priv->panel);251	drm_panel_disable(&priv->panel);252	drm_panel_unprepare(&priv->panel);253}254 255static const struct drm_display_mode ej030na_modes[] = {256	{ /* 60 Hz */257		.clock = 14400,258		.hdisplay = 320,259		.hsync_start = 320 + 10,260		.hsync_end = 320 + 10 + 37,261		.htotal = 320 + 10 + 37 + 33,262		.vdisplay = 480,263		.vsync_start = 480 + 102,264		.vsync_end = 480 + 102 + 9 + 9,265		.vtotal = 480 + 102 + 9 + 9,266		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,267	},268	{ /* 50 Hz */269		.clock = 12000,270		.hdisplay = 320,271		.hsync_start = 320 + 10,272		.hsync_end = 320 + 10 + 37,273		.htotal = 320 + 10 + 37 + 33,274		.vdisplay = 480,275		.vsync_start = 480 + 102,276		.vsync_end = 480 + 102 + 9,277		.vtotal = 480 + 102 + 9 + 9,278		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,279	},280};281 282static const struct ej030na_info ej030na_info = {283	.display_modes = ej030na_modes,284	.num_modes = ARRAY_SIZE(ej030na_modes),285	.width_mm = 70,286	.height_mm = 51,287	.bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA,288	.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_LOW,289};290 291static const struct of_device_id ej030na_of_match[] = {292	{ .compatible = "innolux,ej030na", .data = &ej030na_info },293	{ /* sentinel */ }294};295MODULE_DEVICE_TABLE(of, ej030na_of_match);296 297static struct spi_driver ej030na_driver = {298	.driver = {299		.name = "panel-innolux-ej030na",300		.of_match_table = ej030na_of_match,301	},302	.probe = ej030na_probe,303	.remove = ej030na_remove,304};305module_spi_driver(ej030na_driver);306 307MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");308MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");309MODULE_DESCRIPTION("Innolux/Chimei EJ030NA TFT LCD panel driver");310MODULE_LICENSE("GPL v2");311