162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Innolux/Chimei EJ030NA TFT LCD panel driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> 662306a36Sopenharmony_ci * Copyright (C) 2020, Christophe Branchereau <cbranchereau@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/media-bus-format.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1862306a36Sopenharmony_ci#include <linux/spi/spi.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <drm/drm_modes.h> 2162306a36Sopenharmony_ci#include <drm/drm_panel.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct ej030na_info { 2462306a36Sopenharmony_ci const struct drm_display_mode *display_modes; 2562306a36Sopenharmony_ci unsigned int num_modes; 2662306a36Sopenharmony_ci u16 width_mm, height_mm; 2762306a36Sopenharmony_ci u32 bus_format, bus_flags; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct ej030na { 3162306a36Sopenharmony_ci struct drm_panel panel; 3262306a36Sopenharmony_ci struct spi_device *spi; 3362306a36Sopenharmony_ci struct regmap *map; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci const struct ej030na_info *panel_info; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci struct regulator *supply; 3862306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline struct ej030na *to_ej030na(struct drm_panel *panel) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return container_of(panel, struct ej030na, panel); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const struct reg_sequence ej030na_init_sequence[] = { 4762306a36Sopenharmony_ci { 0x05, 0x1e }, 4862306a36Sopenharmony_ci { 0x05, 0x5c }, 4962306a36Sopenharmony_ci { 0x02, 0x14 }, 5062306a36Sopenharmony_ci { 0x03, 0x40 }, 5162306a36Sopenharmony_ci { 0x04, 0x07 }, 5262306a36Sopenharmony_ci { 0x06, 0x12 }, 5362306a36Sopenharmony_ci { 0x07, 0xd2 }, 5462306a36Sopenharmony_ci { 0x0c, 0x06 }, 5562306a36Sopenharmony_ci { 0x0d, 0x40 }, 5662306a36Sopenharmony_ci { 0x0e, 0x40 }, 5762306a36Sopenharmony_ci { 0x0f, 0x40 }, 5862306a36Sopenharmony_ci { 0x10, 0x40 }, 5962306a36Sopenharmony_ci { 0x11, 0x40 }, 6062306a36Sopenharmony_ci { 0x2f, 0x40 }, 6162306a36Sopenharmony_ci { 0x5a, 0x02 }, 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci { 0x30, 0x07 }, 6462306a36Sopenharmony_ci { 0x31, 0x57 }, 6562306a36Sopenharmony_ci { 0x32, 0x53 }, 6662306a36Sopenharmony_ci { 0x33, 0x77 }, 6762306a36Sopenharmony_ci { 0x34, 0xb8 }, 6862306a36Sopenharmony_ci { 0x35, 0xbd }, 6962306a36Sopenharmony_ci { 0x36, 0xb8 }, 7062306a36Sopenharmony_ci { 0x37, 0xe7 }, 7162306a36Sopenharmony_ci { 0x38, 0x04 }, 7262306a36Sopenharmony_ci { 0x39, 0xff }, 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci { 0x40, 0x0b }, 7562306a36Sopenharmony_ci { 0x41, 0xb8 }, 7662306a36Sopenharmony_ci { 0x42, 0xab }, 7762306a36Sopenharmony_ci { 0x43, 0xb9 }, 7862306a36Sopenharmony_ci { 0x44, 0x6a }, 7962306a36Sopenharmony_ci { 0x45, 0x56 }, 8062306a36Sopenharmony_ci { 0x46, 0x61 }, 8162306a36Sopenharmony_ci { 0x47, 0x08 }, 8262306a36Sopenharmony_ci { 0x48, 0x0f }, 8362306a36Sopenharmony_ci { 0x49, 0x0f }, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int ej030na_prepare(struct drm_panel *panel) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct ej030na *priv = to_ej030na(panel); 8962306a36Sopenharmony_ci struct device *dev = &priv->spi->dev; 9062306a36Sopenharmony_ci int err; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci err = regulator_enable(priv->supply); 9362306a36Sopenharmony_ci if (err) { 9462306a36Sopenharmony_ci dev_err(dev, "Failed to enable power supply: %d\n", err); 9562306a36Sopenharmony_ci return err; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Reset the chip */ 9962306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 10062306a36Sopenharmony_ci usleep_range(50, 150); 10162306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 0); 10262306a36Sopenharmony_ci usleep_range(50, 150); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci err = regmap_multi_reg_write(priv->map, ej030na_init_sequence, 10562306a36Sopenharmony_ci ARRAY_SIZE(ej030na_init_sequence)); 10662306a36Sopenharmony_ci if (err) { 10762306a36Sopenharmony_ci dev_err(dev, "Failed to init registers: %d\n", err); 10862306a36Sopenharmony_ci goto err_disable_regulator; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cierr_disable_regulator: 11462306a36Sopenharmony_ci regulator_disable(priv->supply); 11562306a36Sopenharmony_ci return err; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int ej030na_unprepare(struct drm_panel *panel) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct ej030na *priv = to_ej030na(panel); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 12362306a36Sopenharmony_ci regulator_disable(priv->supply); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int ej030na_enable(struct drm_panel *panel) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct ej030na *priv = to_ej030na(panel); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* standby off */ 13362306a36Sopenharmony_ci regmap_write(priv->map, 0x2b, 0x01); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (panel->backlight) { 13662306a36Sopenharmony_ci /* Wait for the picture to be ready before enabling backlight */ 13762306a36Sopenharmony_ci msleep(120); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int ej030na_disable(struct drm_panel *panel) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct ej030na *priv = to_ej030na(panel); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* standby on */ 14862306a36Sopenharmony_ci regmap_write(priv->map, 0x2b, 0x00); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int ej030na_get_modes(struct drm_panel *panel, 15462306a36Sopenharmony_ci struct drm_connector *connector) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct ej030na *priv = to_ej030na(panel); 15762306a36Sopenharmony_ci const struct ej030na_info *panel_info = priv->panel_info; 15862306a36Sopenharmony_ci struct drm_display_mode *mode; 15962306a36Sopenharmony_ci unsigned int i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < panel_info->num_modes; i++) { 16262306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, 16362306a36Sopenharmony_ci &panel_info->display_modes[i]); 16462306a36Sopenharmony_ci if (!mode) 16562306a36Sopenharmony_ci return -ENOMEM; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci drm_mode_set_name(mode); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER; 17062306a36Sopenharmony_ci if (panel_info->num_modes == 1) 17162306a36Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci connector->display_info.bpc = 8; 17762306a36Sopenharmony_ci connector->display_info.width_mm = panel_info->width_mm; 17862306a36Sopenharmony_ci connector->display_info.height_mm = panel_info->height_mm; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci drm_display_info_set_bus_formats(&connector->display_info, 18162306a36Sopenharmony_ci &panel_info->bus_format, 1); 18262306a36Sopenharmony_ci connector->display_info.bus_flags = panel_info->bus_flags; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return panel_info->num_modes; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct drm_panel_funcs ej030na_funcs = { 18862306a36Sopenharmony_ci .prepare = ej030na_prepare, 18962306a36Sopenharmony_ci .unprepare = ej030na_unprepare, 19062306a36Sopenharmony_ci .enable = ej030na_enable, 19162306a36Sopenharmony_ci .disable = ej030na_disable, 19262306a36Sopenharmony_ci .get_modes = ej030na_get_modes, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const struct regmap_config ej030na_regmap_config = { 19662306a36Sopenharmony_ci .reg_bits = 8, 19762306a36Sopenharmony_ci .val_bits = 8, 19862306a36Sopenharmony_ci .max_register = 0x5a, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int ej030na_probe(struct spi_device *spi) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct device *dev = &spi->dev; 20462306a36Sopenharmony_ci struct ej030na *priv; 20562306a36Sopenharmony_ci int err; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 20862306a36Sopenharmony_ci if (!priv) 20962306a36Sopenharmony_ci return -ENOMEM; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci priv->spi = spi; 21262306a36Sopenharmony_ci spi_set_drvdata(spi, priv); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci priv->map = devm_regmap_init_spi(spi, &ej030na_regmap_config); 21562306a36Sopenharmony_ci if (IS_ERR(priv->map)) { 21662306a36Sopenharmony_ci dev_err(dev, "Unable to init regmap\n"); 21762306a36Sopenharmony_ci return PTR_ERR(priv->map); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci priv->panel_info = of_device_get_match_data(dev); 22162306a36Sopenharmony_ci if (!priv->panel_info) 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci priv->supply = devm_regulator_get(dev, "power"); 22562306a36Sopenharmony_ci if (IS_ERR(priv->supply)) 22662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->supply), 22762306a36Sopenharmony_ci "Failed to get power supply\n"); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 23062306a36Sopenharmony_ci if (IS_ERR(priv->reset_gpio)) 23162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), 23262306a36Sopenharmony_ci "Failed to get reset GPIO\n"); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci drm_panel_init(&priv->panel, dev, &ej030na_funcs, 23562306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DPI); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci err = drm_panel_of_backlight(&priv->panel); 23862306a36Sopenharmony_ci if (err) 23962306a36Sopenharmony_ci return err; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci drm_panel_add(&priv->panel); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void ej030na_remove(struct spi_device *spi) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct ej030na *priv = spi_get_drvdata(spi); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci drm_panel_remove(&priv->panel); 25162306a36Sopenharmony_ci drm_panel_disable(&priv->panel); 25262306a36Sopenharmony_ci drm_panel_unprepare(&priv->panel); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic const struct drm_display_mode ej030na_modes[] = { 25662306a36Sopenharmony_ci { /* 60 Hz */ 25762306a36Sopenharmony_ci .clock = 14400, 25862306a36Sopenharmony_ci .hdisplay = 320, 25962306a36Sopenharmony_ci .hsync_start = 320 + 10, 26062306a36Sopenharmony_ci .hsync_end = 320 + 10 + 37, 26162306a36Sopenharmony_ci .htotal = 320 + 10 + 37 + 33, 26262306a36Sopenharmony_ci .vdisplay = 480, 26362306a36Sopenharmony_ci .vsync_start = 480 + 102, 26462306a36Sopenharmony_ci .vsync_end = 480 + 102 + 9 + 9, 26562306a36Sopenharmony_ci .vtotal = 480 + 102 + 9 + 9, 26662306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 26762306a36Sopenharmony_ci }, 26862306a36Sopenharmony_ci { /* 50 Hz */ 26962306a36Sopenharmony_ci .clock = 12000, 27062306a36Sopenharmony_ci .hdisplay = 320, 27162306a36Sopenharmony_ci .hsync_start = 320 + 10, 27262306a36Sopenharmony_ci .hsync_end = 320 + 10 + 37, 27362306a36Sopenharmony_ci .htotal = 320 + 10 + 37 + 33, 27462306a36Sopenharmony_ci .vdisplay = 480, 27562306a36Sopenharmony_ci .vsync_start = 480 + 102, 27662306a36Sopenharmony_ci .vsync_end = 480 + 102 + 9, 27762306a36Sopenharmony_ci .vtotal = 480 + 102 + 9 + 9, 27862306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 27962306a36Sopenharmony_ci }, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const struct ej030na_info ej030na_info = { 28362306a36Sopenharmony_ci .display_modes = ej030na_modes, 28462306a36Sopenharmony_ci .num_modes = ARRAY_SIZE(ej030na_modes), 28562306a36Sopenharmony_ci .width_mm = 70, 28662306a36Sopenharmony_ci .height_mm = 51, 28762306a36Sopenharmony_ci .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, 28862306a36Sopenharmony_ci .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_LOW, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct of_device_id ej030na_of_match[] = { 29262306a36Sopenharmony_ci { .compatible = "innolux,ej030na", .data = &ej030na_info }, 29362306a36Sopenharmony_ci { /* sentinel */ } 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ej030na_of_match); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic struct spi_driver ej030na_driver = { 29862306a36Sopenharmony_ci .driver = { 29962306a36Sopenharmony_ci .name = "panel-innolux-ej030na", 30062306a36Sopenharmony_ci .of_match_table = ej030na_of_match, 30162306a36Sopenharmony_ci }, 30262306a36Sopenharmony_ci .probe = ej030na_probe, 30362306a36Sopenharmony_ci .remove = ej030na_remove, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_cimodule_spi_driver(ej030na_driver); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 30862306a36Sopenharmony_ciMODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); 30962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 310