162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NewVision NV3052C IPS LCD panel driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> 662306a36Sopenharmony_ci * Copyright (C) 2022, 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/regulator/consumer.h> 1762306a36Sopenharmony_ci#include <linux/spi/spi.h> 1862306a36Sopenharmony_ci#include <video/mipi_display.h> 1962306a36Sopenharmony_ci#include <drm/drm_mipi_dbi.h> 2062306a36Sopenharmony_ci#include <drm/drm_modes.h> 2162306a36Sopenharmony_ci#include <drm/drm_panel.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct nv3052c_panel_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 nv3052c { 3162306a36Sopenharmony_ci struct device *dev; 3262306a36Sopenharmony_ci struct drm_panel panel; 3362306a36Sopenharmony_ci struct mipi_dbi dbi; 3462306a36Sopenharmony_ci const struct nv3052c_panel_info *panel_info; 3562306a36Sopenharmony_ci struct regulator *supply; 3662306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct nv3052c_reg { 4062306a36Sopenharmony_ci u8 cmd; 4162306a36Sopenharmony_ci u8 val; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const struct nv3052c_reg nv3052c_panel_regs[] = { 4562306a36Sopenharmony_ci { 0xff, 0x30 }, 4662306a36Sopenharmony_ci { 0xff, 0x52 }, 4762306a36Sopenharmony_ci { 0xff, 0x01 }, 4862306a36Sopenharmony_ci { 0xe3, 0x00 }, 4962306a36Sopenharmony_ci { 0x40, 0x00 }, 5062306a36Sopenharmony_ci { 0x03, 0x40 }, 5162306a36Sopenharmony_ci { 0x04, 0x00 }, 5262306a36Sopenharmony_ci { 0x05, 0x03 }, 5362306a36Sopenharmony_ci { 0x08, 0x00 }, 5462306a36Sopenharmony_ci { 0x09, 0x07 }, 5562306a36Sopenharmony_ci { 0x0a, 0x01 }, 5662306a36Sopenharmony_ci { 0x0b, 0x32 }, 5762306a36Sopenharmony_ci { 0x0c, 0x32 }, 5862306a36Sopenharmony_ci { 0x0d, 0x0b }, 5962306a36Sopenharmony_ci { 0x0e, 0x00 }, 6062306a36Sopenharmony_ci { 0x23, 0xa0 }, 6162306a36Sopenharmony_ci { 0x24, 0x0c }, 6262306a36Sopenharmony_ci { 0x25, 0x06 }, 6362306a36Sopenharmony_ci { 0x26, 0x14 }, 6462306a36Sopenharmony_ci { 0x27, 0x14 }, 6562306a36Sopenharmony_ci { 0x38, 0xcc }, 6662306a36Sopenharmony_ci { 0x39, 0xd7 }, 6762306a36Sopenharmony_ci { 0x3a, 0x4a }, 6862306a36Sopenharmony_ci { 0x28, 0x40 }, 6962306a36Sopenharmony_ci { 0x29, 0x01 }, 7062306a36Sopenharmony_ci { 0x2a, 0xdf }, 7162306a36Sopenharmony_ci { 0x49, 0x3c }, 7262306a36Sopenharmony_ci { 0x91, 0x77 }, 7362306a36Sopenharmony_ci { 0x92, 0x77 }, 7462306a36Sopenharmony_ci { 0xa0, 0x55 }, 7562306a36Sopenharmony_ci { 0xa1, 0x50 }, 7662306a36Sopenharmony_ci { 0xa4, 0x9c }, 7762306a36Sopenharmony_ci { 0xa7, 0x02 }, 7862306a36Sopenharmony_ci { 0xa8, 0x01 }, 7962306a36Sopenharmony_ci { 0xa9, 0x01 }, 8062306a36Sopenharmony_ci { 0xaa, 0xfc }, 8162306a36Sopenharmony_ci { 0xab, 0x28 }, 8262306a36Sopenharmony_ci { 0xac, 0x06 }, 8362306a36Sopenharmony_ci { 0xad, 0x06 }, 8462306a36Sopenharmony_ci { 0xae, 0x06 }, 8562306a36Sopenharmony_ci { 0xaf, 0x03 }, 8662306a36Sopenharmony_ci { 0xb0, 0x08 }, 8762306a36Sopenharmony_ci { 0xb1, 0x26 }, 8862306a36Sopenharmony_ci { 0xb2, 0x28 }, 8962306a36Sopenharmony_ci { 0xb3, 0x28 }, 9062306a36Sopenharmony_ci { 0xb4, 0x33 }, 9162306a36Sopenharmony_ci { 0xb5, 0x08 }, 9262306a36Sopenharmony_ci { 0xb6, 0x26 }, 9362306a36Sopenharmony_ci { 0xb7, 0x08 }, 9462306a36Sopenharmony_ci { 0xb8, 0x26 }, 9562306a36Sopenharmony_ci { 0xf0, 0x00 }, 9662306a36Sopenharmony_ci { 0xf6, 0xc0 }, 9762306a36Sopenharmony_ci { 0xff, 0x30 }, 9862306a36Sopenharmony_ci { 0xff, 0x52 }, 9962306a36Sopenharmony_ci { 0xff, 0x02 }, 10062306a36Sopenharmony_ci { 0xb0, 0x0b }, 10162306a36Sopenharmony_ci { 0xb1, 0x16 }, 10262306a36Sopenharmony_ci { 0xb2, 0x17 }, 10362306a36Sopenharmony_ci { 0xb3, 0x2c }, 10462306a36Sopenharmony_ci { 0xb4, 0x32 }, 10562306a36Sopenharmony_ci { 0xb5, 0x3b }, 10662306a36Sopenharmony_ci { 0xb6, 0x29 }, 10762306a36Sopenharmony_ci { 0xb7, 0x40 }, 10862306a36Sopenharmony_ci { 0xb8, 0x0d }, 10962306a36Sopenharmony_ci { 0xb9, 0x05 }, 11062306a36Sopenharmony_ci { 0xba, 0x12 }, 11162306a36Sopenharmony_ci { 0xbb, 0x10 }, 11262306a36Sopenharmony_ci { 0xbc, 0x12 }, 11362306a36Sopenharmony_ci { 0xbd, 0x15 }, 11462306a36Sopenharmony_ci { 0xbe, 0x19 }, 11562306a36Sopenharmony_ci { 0xbf, 0x0e }, 11662306a36Sopenharmony_ci { 0xc0, 0x16 }, 11762306a36Sopenharmony_ci { 0xc1, 0x0a }, 11862306a36Sopenharmony_ci { 0xd0, 0x0c }, 11962306a36Sopenharmony_ci { 0xd1, 0x17 }, 12062306a36Sopenharmony_ci { 0xd2, 0x14 }, 12162306a36Sopenharmony_ci { 0xd3, 0x2e }, 12262306a36Sopenharmony_ci { 0xd4, 0x32 }, 12362306a36Sopenharmony_ci { 0xd5, 0x3c }, 12462306a36Sopenharmony_ci { 0xd6, 0x22 }, 12562306a36Sopenharmony_ci { 0xd7, 0x3d }, 12662306a36Sopenharmony_ci { 0xd8, 0x0d }, 12762306a36Sopenharmony_ci { 0xd9, 0x07 }, 12862306a36Sopenharmony_ci { 0xda, 0x13 }, 12962306a36Sopenharmony_ci { 0xdb, 0x13 }, 13062306a36Sopenharmony_ci { 0xdc, 0x11 }, 13162306a36Sopenharmony_ci { 0xdd, 0x15 }, 13262306a36Sopenharmony_ci { 0xde, 0x19 }, 13362306a36Sopenharmony_ci { 0xdf, 0x10 }, 13462306a36Sopenharmony_ci { 0xe0, 0x17 }, 13562306a36Sopenharmony_ci { 0xe1, 0x0a }, 13662306a36Sopenharmony_ci { 0xff, 0x30 }, 13762306a36Sopenharmony_ci { 0xff, 0x52 }, 13862306a36Sopenharmony_ci { 0xff, 0x03 }, 13962306a36Sopenharmony_ci { 0x00, 0x2a }, 14062306a36Sopenharmony_ci { 0x01, 0x2a }, 14162306a36Sopenharmony_ci { 0x02, 0x2a }, 14262306a36Sopenharmony_ci { 0x03, 0x2a }, 14362306a36Sopenharmony_ci { 0x04, 0x61 }, 14462306a36Sopenharmony_ci { 0x05, 0x80 }, 14562306a36Sopenharmony_ci { 0x06, 0xc7 }, 14662306a36Sopenharmony_ci { 0x07, 0x01 }, 14762306a36Sopenharmony_ci { 0x08, 0x03 }, 14862306a36Sopenharmony_ci { 0x09, 0x04 }, 14962306a36Sopenharmony_ci { 0x70, 0x22 }, 15062306a36Sopenharmony_ci { 0x71, 0x80 }, 15162306a36Sopenharmony_ci { 0x30, 0x2a }, 15262306a36Sopenharmony_ci { 0x31, 0x2a }, 15362306a36Sopenharmony_ci { 0x32, 0x2a }, 15462306a36Sopenharmony_ci { 0x33, 0x2a }, 15562306a36Sopenharmony_ci { 0x34, 0x61 }, 15662306a36Sopenharmony_ci { 0x35, 0xc5 }, 15762306a36Sopenharmony_ci { 0x36, 0x80 }, 15862306a36Sopenharmony_ci { 0x37, 0x23 }, 15962306a36Sopenharmony_ci { 0x40, 0x03 }, 16062306a36Sopenharmony_ci { 0x41, 0x04 }, 16162306a36Sopenharmony_ci { 0x42, 0x05 }, 16262306a36Sopenharmony_ci { 0x43, 0x06 }, 16362306a36Sopenharmony_ci { 0x44, 0x11 }, 16462306a36Sopenharmony_ci { 0x45, 0xe8 }, 16562306a36Sopenharmony_ci { 0x46, 0xe9 }, 16662306a36Sopenharmony_ci { 0x47, 0x11 }, 16762306a36Sopenharmony_ci { 0x48, 0xea }, 16862306a36Sopenharmony_ci { 0x49, 0xeb }, 16962306a36Sopenharmony_ci { 0x50, 0x07 }, 17062306a36Sopenharmony_ci { 0x51, 0x08 }, 17162306a36Sopenharmony_ci { 0x52, 0x09 }, 17262306a36Sopenharmony_ci { 0x53, 0x0a }, 17362306a36Sopenharmony_ci { 0x54, 0x11 }, 17462306a36Sopenharmony_ci { 0x55, 0xec }, 17562306a36Sopenharmony_ci { 0x56, 0xed }, 17662306a36Sopenharmony_ci { 0x57, 0x11 }, 17762306a36Sopenharmony_ci { 0x58, 0xef }, 17862306a36Sopenharmony_ci { 0x59, 0xf0 }, 17962306a36Sopenharmony_ci { 0xb1, 0x01 }, 18062306a36Sopenharmony_ci { 0xb4, 0x15 }, 18162306a36Sopenharmony_ci { 0xb5, 0x16 }, 18262306a36Sopenharmony_ci { 0xb6, 0x09 }, 18362306a36Sopenharmony_ci { 0xb7, 0x0f }, 18462306a36Sopenharmony_ci { 0xb8, 0x0d }, 18562306a36Sopenharmony_ci { 0xb9, 0x0b }, 18662306a36Sopenharmony_ci { 0xba, 0x00 }, 18762306a36Sopenharmony_ci { 0xc7, 0x02 }, 18862306a36Sopenharmony_ci { 0xca, 0x17 }, 18962306a36Sopenharmony_ci { 0xcb, 0x18 }, 19062306a36Sopenharmony_ci { 0xcc, 0x0a }, 19162306a36Sopenharmony_ci { 0xcd, 0x10 }, 19262306a36Sopenharmony_ci { 0xce, 0x0e }, 19362306a36Sopenharmony_ci { 0xcf, 0x0c }, 19462306a36Sopenharmony_ci { 0xd0, 0x00 }, 19562306a36Sopenharmony_ci { 0x81, 0x00 }, 19662306a36Sopenharmony_ci { 0x84, 0x15 }, 19762306a36Sopenharmony_ci { 0x85, 0x16 }, 19862306a36Sopenharmony_ci { 0x86, 0x10 }, 19962306a36Sopenharmony_ci { 0x87, 0x0a }, 20062306a36Sopenharmony_ci { 0x88, 0x0c }, 20162306a36Sopenharmony_ci { 0x89, 0x0e }, 20262306a36Sopenharmony_ci { 0x8a, 0x02 }, 20362306a36Sopenharmony_ci { 0x97, 0x00 }, 20462306a36Sopenharmony_ci { 0x9a, 0x17 }, 20562306a36Sopenharmony_ci { 0x9b, 0x18 }, 20662306a36Sopenharmony_ci { 0x9c, 0x0f }, 20762306a36Sopenharmony_ci { 0x9d, 0x09 }, 20862306a36Sopenharmony_ci { 0x9e, 0x0b }, 20962306a36Sopenharmony_ci { 0x9f, 0x0d }, 21062306a36Sopenharmony_ci { 0xa0, 0x01 }, 21162306a36Sopenharmony_ci { 0xff, 0x30 }, 21262306a36Sopenharmony_ci { 0xff, 0x52 }, 21362306a36Sopenharmony_ci { 0xff, 0x02 }, 21462306a36Sopenharmony_ci { 0x01, 0x01 }, 21562306a36Sopenharmony_ci { 0x02, 0xda }, 21662306a36Sopenharmony_ci { 0x03, 0xba }, 21762306a36Sopenharmony_ci { 0x04, 0xa8 }, 21862306a36Sopenharmony_ci { 0x05, 0x9a }, 21962306a36Sopenharmony_ci { 0x06, 0x70 }, 22062306a36Sopenharmony_ci { 0x07, 0xff }, 22162306a36Sopenharmony_ci { 0x08, 0x91 }, 22262306a36Sopenharmony_ci { 0x09, 0x90 }, 22362306a36Sopenharmony_ci { 0x0a, 0xff }, 22462306a36Sopenharmony_ci { 0x0b, 0x8f }, 22562306a36Sopenharmony_ci { 0x0c, 0x60 }, 22662306a36Sopenharmony_ci { 0x0d, 0x58 }, 22762306a36Sopenharmony_ci { 0x0e, 0x48 }, 22862306a36Sopenharmony_ci { 0x0f, 0x38 }, 22962306a36Sopenharmony_ci { 0x10, 0x2b }, 23062306a36Sopenharmony_ci { 0xff, 0x30 }, 23162306a36Sopenharmony_ci { 0xff, 0x52 }, 23262306a36Sopenharmony_ci { 0xff, 0x00 }, 23362306a36Sopenharmony_ci { 0x36, 0x0a }, 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic inline struct nv3052c *to_nv3052c(struct drm_panel *panel) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return container_of(panel, struct nv3052c, panel); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int nv3052c_prepare(struct drm_panel *panel) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct nv3052c *priv = to_nv3052c(panel); 24462306a36Sopenharmony_ci struct mipi_dbi *dbi = &priv->dbi; 24562306a36Sopenharmony_ci unsigned int i; 24662306a36Sopenharmony_ci int err; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci err = regulator_enable(priv->supply); 24962306a36Sopenharmony_ci if (err) { 25062306a36Sopenharmony_ci dev_err(priv->dev, "Failed to enable power supply: %d\n", err); 25162306a36Sopenharmony_ci return err; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Reset the chip */ 25562306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 25662306a36Sopenharmony_ci usleep_range(10, 1000); 25762306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 0); 25862306a36Sopenharmony_ci usleep_range(5000, 20000); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nv3052c_panel_regs); i++) { 26162306a36Sopenharmony_ci err = mipi_dbi_command(dbi, nv3052c_panel_regs[i].cmd, 26262306a36Sopenharmony_ci nv3052c_panel_regs[i].val); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (err) { 26562306a36Sopenharmony_ci dev_err(priv->dev, "Unable to set register: %d\n", err); 26662306a36Sopenharmony_ci goto err_disable_regulator; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 27162306a36Sopenharmony_ci if (err) { 27262306a36Sopenharmony_ci dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err); 27362306a36Sopenharmony_ci goto err_disable_regulator; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cierr_disable_regulator: 27962306a36Sopenharmony_ci regulator_disable(priv->supply); 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int nv3052c_unprepare(struct drm_panel *panel) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct nv3052c *priv = to_nv3052c(panel); 28662306a36Sopenharmony_ci struct mipi_dbi *dbi = &priv->dbi; 28762306a36Sopenharmony_ci int err; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); 29062306a36Sopenharmony_ci if (err) 29162306a36Sopenharmony_ci dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 29462306a36Sopenharmony_ci regulator_disable(priv->supply); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int nv3052c_enable(struct drm_panel *panel) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct nv3052c *priv = to_nv3052c(panel); 30262306a36Sopenharmony_ci struct mipi_dbi *dbi = &priv->dbi; 30362306a36Sopenharmony_ci int err; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 30662306a36Sopenharmony_ci if (err) { 30762306a36Sopenharmony_ci dev_err(priv->dev, "Unable to enable display: %d\n", err); 30862306a36Sopenharmony_ci return err; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (panel->backlight) { 31262306a36Sopenharmony_ci /* Wait for the picture to be ready before enabling backlight */ 31362306a36Sopenharmony_ci msleep(120); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int nv3052c_disable(struct drm_panel *panel) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct nv3052c *priv = to_nv3052c(panel); 32262306a36Sopenharmony_ci struct mipi_dbi *dbi = &priv->dbi; 32362306a36Sopenharmony_ci int err; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 32662306a36Sopenharmony_ci if (err) { 32762306a36Sopenharmony_ci dev_err(priv->dev, "Unable to disable display: %d\n", err); 32862306a36Sopenharmony_ci return err; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int nv3052c_get_modes(struct drm_panel *panel, 33562306a36Sopenharmony_ci struct drm_connector *connector) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct nv3052c *priv = to_nv3052c(panel); 33862306a36Sopenharmony_ci const struct nv3052c_panel_info *panel_info = priv->panel_info; 33962306a36Sopenharmony_ci struct drm_display_mode *mode; 34062306a36Sopenharmony_ci unsigned int i; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < panel_info->num_modes; i++) { 34362306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, 34462306a36Sopenharmony_ci &panel_info->display_modes[i]); 34562306a36Sopenharmony_ci if (!mode) 34662306a36Sopenharmony_ci return -ENOMEM; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci drm_mode_set_name(mode); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER; 35162306a36Sopenharmony_ci if (panel_info->num_modes == 1) 35262306a36Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci connector->display_info.bpc = 8; 35862306a36Sopenharmony_ci connector->display_info.width_mm = panel_info->width_mm; 35962306a36Sopenharmony_ci connector->display_info.height_mm = panel_info->height_mm; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci drm_display_info_set_bus_formats(&connector->display_info, 36262306a36Sopenharmony_ci &panel_info->bus_format, 1); 36362306a36Sopenharmony_ci connector->display_info.bus_flags = panel_info->bus_flags; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return panel_info->num_modes; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct drm_panel_funcs nv3052c_funcs = { 36962306a36Sopenharmony_ci .prepare = nv3052c_prepare, 37062306a36Sopenharmony_ci .unprepare = nv3052c_unprepare, 37162306a36Sopenharmony_ci .enable = nv3052c_enable, 37262306a36Sopenharmony_ci .disable = nv3052c_disable, 37362306a36Sopenharmony_ci .get_modes = nv3052c_get_modes, 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int nv3052c_probe(struct spi_device *spi) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct device *dev = &spi->dev; 37962306a36Sopenharmony_ci struct nv3052c *priv; 38062306a36Sopenharmony_ci int err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 38362306a36Sopenharmony_ci if (!priv) 38462306a36Sopenharmony_ci return -ENOMEM; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci priv->dev = dev; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci priv->panel_info = of_device_get_match_data(dev); 38962306a36Sopenharmony_ci if (!priv->panel_info) 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci priv->supply = devm_regulator_get(dev, "power"); 39362306a36Sopenharmony_ci if (IS_ERR(priv->supply)) 39462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n"); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 39762306a36Sopenharmony_ci if (IS_ERR(priv->reset_gpio)) 39862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci err = mipi_dbi_spi_init(spi, &priv->dbi, NULL); 40162306a36Sopenharmony_ci if (err) 40262306a36Sopenharmony_ci return dev_err_probe(dev, err, "MIPI DBI init failed\n"); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci priv->dbi.read_commands = NULL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci spi_set_drvdata(spi, priv); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci drm_panel_init(&priv->panel, dev, &nv3052c_funcs, 40962306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DPI); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci err = drm_panel_of_backlight(&priv->panel); 41262306a36Sopenharmony_ci if (err) 41362306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to attach backlight\n"); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci drm_panel_add(&priv->panel); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void nv3052c_remove(struct spi_device *spi) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct nv3052c *priv = spi_get_drvdata(spi); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci drm_panel_remove(&priv->panel); 42562306a36Sopenharmony_ci drm_panel_disable(&priv->panel); 42662306a36Sopenharmony_ci drm_panel_unprepare(&priv->panel); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic const struct drm_display_mode ltk035c5444t_modes[] = { 43062306a36Sopenharmony_ci { /* 60 Hz */ 43162306a36Sopenharmony_ci .clock = 24000, 43262306a36Sopenharmony_ci .hdisplay = 640, 43362306a36Sopenharmony_ci .hsync_start = 640 + 96, 43462306a36Sopenharmony_ci .hsync_end = 640 + 96 + 16, 43562306a36Sopenharmony_ci .htotal = 640 + 96 + 16 + 48, 43662306a36Sopenharmony_ci .vdisplay = 480, 43762306a36Sopenharmony_ci .vsync_start = 480 + 5, 43862306a36Sopenharmony_ci .vsync_end = 480 + 5 + 2, 43962306a36Sopenharmony_ci .vtotal = 480 + 5 + 2 + 13, 44062306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 44162306a36Sopenharmony_ci }, 44262306a36Sopenharmony_ci { /* 50 Hz */ 44362306a36Sopenharmony_ci .clock = 18000, 44462306a36Sopenharmony_ci .hdisplay = 640, 44562306a36Sopenharmony_ci .hsync_start = 640 + 39, 44662306a36Sopenharmony_ci .hsync_end = 640 + 39 + 2, 44762306a36Sopenharmony_ci .htotal = 640 + 39 + 2 + 39, 44862306a36Sopenharmony_ci .vdisplay = 480, 44962306a36Sopenharmony_ci .vsync_start = 480 + 5, 45062306a36Sopenharmony_ci .vsync_end = 480 + 5 + 2, 45162306a36Sopenharmony_ci .vtotal = 480 + 5 + 2 + 13, 45262306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 45362306a36Sopenharmony_ci }, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic const struct nv3052c_panel_info ltk035c5444t_panel_info = { 45762306a36Sopenharmony_ci .display_modes = ltk035c5444t_modes, 45862306a36Sopenharmony_ci .num_modes = ARRAY_SIZE(ltk035c5444t_modes), 45962306a36Sopenharmony_ci .width_mm = 77, 46062306a36Sopenharmony_ci .height_mm = 64, 46162306a36Sopenharmony_ci .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 46262306a36Sopenharmony_ci .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic const struct of_device_id nv3052c_of_match[] = { 46662306a36Sopenharmony_ci { .compatible = "leadtek,ltk035c5444t", .data = <k035c5444t_panel_info }, 46762306a36Sopenharmony_ci { /* sentinel */ } 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nv3052c_of_match); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic struct spi_driver nv3052c_driver = { 47262306a36Sopenharmony_ci .driver = { 47362306a36Sopenharmony_ci .name = "nv3052c", 47462306a36Sopenharmony_ci .of_match_table = nv3052c_of_match, 47562306a36Sopenharmony_ci }, 47662306a36Sopenharmony_ci .probe = nv3052c_probe, 47762306a36Sopenharmony_ci .remove = nv3052c_remove, 47862306a36Sopenharmony_ci}; 47962306a36Sopenharmony_cimodule_spi_driver(nv3052c_driver); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 48262306a36Sopenharmony_ciMODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); 48362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 484