panel-samsung-db7430.c (10040B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Panel driver for the Samsung LMS397KF04 480x800 DPI RGB panel. 4 * According to the data sheet the display controller is called DB7430. 5 * Found in the Samsung Galaxy Beam GT-I8350 mobile phone. 6 * Linus Walleij <linus.walleij@linaro.org> 7 */ 8#include <drm/drm_mipi_dbi.h> 9#include <drm/drm_modes.h> 10#include <drm/drm_panel.h> 11 12#include <linux/delay.h> 13#include <linux/gpio/consumer.h> 14#include <linux/init.h> 15#include <linux/kernel.h> 16#include <linux/media-bus-format.h> 17#include <linux/module.h> 18#include <linux/of.h> 19#include <linux/regulator/consumer.h> 20#include <linux/spi/spi.h> 21 22#include <video/mipi_display.h> 23 24#define DB7430_ACCESS_PROT_OFF 0xb0 25#define DB7430_UNKNOWN_B4 0xb4 26#define DB7430_USER_SELECT 0xb5 27#define DB7430_UNKNOWN_B7 0xb7 28#define DB7430_UNKNOWN_B8 0xb8 29#define DB7430_PANEL_DRIVING 0xc0 30#define DB7430_SOURCE_CONTROL 0xc1 31#define DB7430_GATE_INTERFACE 0xc4 32#define DB7430_DISPLAY_H_TIMING 0xc5 33#define DB7430_RGB_SYNC_OPTION 0xc6 34#define DB7430_GAMMA_SET_RED 0xc8 35#define DB7430_GAMMA_SET_GREEN 0xc9 36#define DB7430_GAMMA_SET_BLUE 0xca 37#define DB7430_BIAS_CURRENT_CTRL 0xd1 38#define DB7430_DDV_CTRL 0xd2 39#define DB7430_GAMMA_CTRL_REF 0xd3 40#define DB7430_UNKNOWN_D4 0xd4 41#define DB7430_DCDC_CTRL 0xd5 42#define DB7430_VCL_CTRL 0xd6 43#define DB7430_UNKNOWN_F8 0xf8 44#define DB7430_UNKNOWN_FC 0xfc 45 46#define DATA_MASK 0x100 47 48/** 49 * struct db7430 - state container for a panel controlled by the DB7430 50 * controller 51 */ 52struct db7430 { 53 /** @dev: the container device */ 54 struct device *dev; 55 /** @dbi: the DBI bus abstraction handle */ 56 struct mipi_dbi dbi; 57 /** @panel: the DRM panel instance for this device */ 58 struct drm_panel panel; 59 /** @width: the width of this panel in mm */ 60 u32 width; 61 /** @height: the height of this panel in mm */ 62 u32 height; 63 /** @reset: reset GPIO line */ 64 struct gpio_desc *reset; 65 /** @regulators: VCCIO and VIO supply regulators */ 66 struct regulator_bulk_data regulators[2]; 67}; 68 69static const struct drm_display_mode db7430_480_800_mode = { 70 /* 71 * 31 ns period min (htotal*vtotal*vrefresh)/1000 72 * gives a Vrefresh of ~71 Hz. 73 */ 74 .clock = 32258, 75 .hdisplay = 480, 76 .hsync_start = 480 + 10, 77 .hsync_end = 480 + 10 + 4, 78 .htotal = 480 + 10 + 4 + 40, 79 .vdisplay = 800, 80 .vsync_start = 800 + 6, 81 .vsync_end = 800 + 6 + 1, 82 .vtotal = 800 + 6 + 1 + 7, 83 .width_mm = 53, 84 .height_mm = 87, 85 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 86}; 87 88static inline struct db7430 *to_db7430(struct drm_panel *panel) 89{ 90 return container_of(panel, struct db7430, panel); 91} 92 93static int db7430_power_on(struct db7430 *db) 94{ 95 struct mipi_dbi *dbi = &db->dbi; 96 int ret; 97 98 /* Power up */ 99 ret = regulator_bulk_enable(ARRAY_SIZE(db->regulators), 100 db->regulators); 101 if (ret) { 102 dev_err(db->dev, "failed to enable regulators: %d\n", ret); 103 return ret; 104 } 105 msleep(50); 106 107 /* Assert reset >=1 ms */ 108 gpiod_set_value_cansleep(db->reset, 1); 109 usleep_range(1000, 5000); 110 /* De-assert reset */ 111 gpiod_set_value_cansleep(db->reset, 0); 112 /* Wait >= 10 ms */ 113 msleep(10); 114 dev_dbg(db->dev, "de-asserted RESET\n"); 115 116 /* 117 * This is set to 0x0a (RGB/BGR order + horizontal flip) in order 118 * to make the display behave normally. If this is not set the displays 119 * normal output behaviour is horizontally flipped and BGR ordered. Do 120 * it twice because the first message doesn't always "take". 121 */ 122 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a); 123 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a); 124 mipi_dbi_command(dbi, DB7430_ACCESS_PROT_OFF, 0x00); 125 mipi_dbi_command(dbi, DB7430_PANEL_DRIVING, 0x28, 0x08); 126 mipi_dbi_command(dbi, DB7430_SOURCE_CONTROL, 127 0x01, 0x30, 0x15, 0x05, 0x22); 128 mipi_dbi_command(dbi, DB7430_GATE_INTERFACE, 129 0x10, 0x01, 0x00); 130 mipi_dbi_command(dbi, DB7430_DISPLAY_H_TIMING, 131 0x06, 0x55, 0x03, 0x07, 0x0b, 132 0x33, 0x00, 0x01, 0x03); 133 /* 134 * 0x00 in datasheet 0x01 in vendor code 0x00, it seems 0x01 means 135 * DE active high and 0x00 means DE active low. 136 */ 137 mipi_dbi_command(dbi, DB7430_RGB_SYNC_OPTION, 0x01); 138 mipi_dbi_command(dbi, DB7430_GAMMA_SET_RED, 139 /* R positive gamma */ 0x00, 140 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62, 141 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08, 142 /* R negative gamma */ 0x00, 143 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62, 144 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08); 145 mipi_dbi_command(dbi, DB7430_GAMMA_SET_GREEN, 146 /* G positive gamma */ 0x00, 147 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59, 148 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A, 149 /* G negative gamma */ 0x00, 150 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59, 151 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A); 152 mipi_dbi_command(dbi, DB7430_GAMMA_SET_BLUE, 153 /* B positive gamma */ 0x00, 154 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D, 155 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C, 156 /* B negative gamma */ 0x00, 157 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D, 158 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C); 159 mipi_dbi_command(dbi, DB7430_BIAS_CURRENT_CTRL, 0x33, 0x13); 160 mipi_dbi_command(dbi, DB7430_DDV_CTRL, 0x11, 0x00, 0x00); 161 mipi_dbi_command(dbi, DB7430_GAMMA_CTRL_REF, 0x50, 0x50); 162 mipi_dbi_command(dbi, DB7430_DCDC_CTRL, 0x2f, 0x11, 0x1e, 0x46); 163 mipi_dbi_command(dbi, DB7430_VCL_CTRL, 0x11, 0x0a); 164 165 return 0; 166} 167 168static int db7430_power_off(struct db7430 *db) 169{ 170 /* Go into RESET and disable regulators */ 171 gpiod_set_value_cansleep(db->reset, 1); 172 return regulator_bulk_disable(ARRAY_SIZE(db->regulators), 173 db->regulators); 174} 175 176static int db7430_unprepare(struct drm_panel *panel) 177{ 178 return db7430_power_off(to_db7430(panel)); 179} 180 181static int db7430_disable(struct drm_panel *panel) 182{ 183 struct db7430 *db = to_db7430(panel); 184 struct mipi_dbi *dbi = &db->dbi; 185 186 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 187 msleep(25); 188 mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); 189 msleep(120); 190 191 return 0; 192} 193 194static int db7430_prepare(struct drm_panel *panel) 195{ 196 return db7430_power_on(to_db7430(panel)); 197} 198 199static int db7430_enable(struct drm_panel *panel) 200{ 201 struct db7430 *db = to_db7430(panel); 202 struct mipi_dbi *dbi = &db->dbi; 203 204 /* Exit sleep mode */ 205 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 206 msleep(20); 207 208 /* NVM (non-volatile memory) load sequence */ 209 mipi_dbi_command(dbi, DB7430_UNKNOWN_D4, 0x52, 0x5e); 210 mipi_dbi_command(dbi, DB7430_UNKNOWN_F8, 0x01, 0xf5, 0xf2, 0x71, 0x44); 211 mipi_dbi_command(dbi, DB7430_UNKNOWN_FC, 0x00, 0x08); 212 msleep(150); 213 214 /* CABC turn on sequence (BC = backlight control) */ 215 mipi_dbi_command(dbi, DB7430_UNKNOWN_B4, 0x0f, 0x00, 0x50); 216 mipi_dbi_command(dbi, DB7430_USER_SELECT, 0x80); 217 mipi_dbi_command(dbi, DB7430_UNKNOWN_B7, 0x24); 218 mipi_dbi_command(dbi, DB7430_UNKNOWN_B8, 0x01); 219 220 /* Turn on display */ 221 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 222 223 return 0; 224} 225 226/** 227 * db7430_get_modes() - return the mode 228 * @panel: the panel to get the mode for 229 * @connector: reference to the central DRM connector control structure 230 */ 231static int db7430_get_modes(struct drm_panel *panel, 232 struct drm_connector *connector) 233{ 234 struct db7430 *db = to_db7430(panel); 235 struct drm_display_mode *mode; 236 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 237 238 mode = drm_mode_duplicate(connector->dev, &db7430_480_800_mode); 239 if (!mode) { 240 dev_err(db->dev, "failed to add mode\n"); 241 return -ENOMEM; 242 } 243 244 connector->display_info.bpc = 8; 245 connector->display_info.width_mm = mode->width_mm; 246 connector->display_info.height_mm = mode->height_mm; 247 connector->display_info.bus_flags = 248 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 249 drm_display_info_set_bus_formats(&connector->display_info, 250 &bus_format, 1); 251 252 drm_mode_set_name(mode); 253 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 254 255 drm_mode_probed_add(connector, mode); 256 257 return 1; 258} 259 260static const struct drm_panel_funcs db7430_drm_funcs = { 261 .disable = db7430_disable, 262 .unprepare = db7430_unprepare, 263 .prepare = db7430_prepare, 264 .enable = db7430_enable, 265 .get_modes = db7430_get_modes, 266}; 267 268static int db7430_probe(struct spi_device *spi) 269{ 270 struct device *dev = &spi->dev; 271 struct db7430 *db; 272 int ret; 273 274 db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL); 275 if (!db) 276 return -ENOMEM; 277 db->dev = dev; 278 279 /* 280 * VCI is the analog voltage supply 281 * VCCIO is the digital I/O voltage supply 282 */ 283 db->regulators[0].supply = "vci"; 284 db->regulators[1].supply = "vccio"; 285 ret = devm_regulator_bulk_get(dev, 286 ARRAY_SIZE(db->regulators), 287 db->regulators); 288 if (ret) 289 return dev_err_probe(dev, ret, "failed to get regulators\n"); 290 291 db->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 292 if (IS_ERR(db->reset)) { 293 ret = PTR_ERR(db->reset); 294 return dev_err_probe(dev, ret, "no RESET GPIO\n"); 295 } 296 297 ret = mipi_dbi_spi_init(spi, &db->dbi, NULL); 298 if (ret) 299 return dev_err_probe(dev, ret, "MIPI DBI init failed\n"); 300 301 drm_panel_init(&db->panel, dev, &db7430_drm_funcs, 302 DRM_MODE_CONNECTOR_DPI); 303 304 /* FIXME: if no external backlight, use internal backlight */ 305 ret = drm_panel_of_backlight(&db->panel); 306 if (ret) 307 return dev_err_probe(dev, ret, "failed to add backlight\n"); 308 309 spi_set_drvdata(spi, db); 310 311 drm_panel_add(&db->panel); 312 dev_dbg(dev, "added panel\n"); 313 314 return 0; 315} 316 317static void db7430_remove(struct spi_device *spi) 318{ 319 struct db7430 *db = spi_get_drvdata(spi); 320 321 drm_panel_remove(&db->panel); 322} 323 324/* 325 * The DB7430 display controller may be used in several display products, 326 * so list the different variants here and add per-variant data if needed. 327 */ 328static const struct of_device_id db7430_match[] = { 329 { .compatible = "samsung,lms397kf04", }, 330 {}, 331}; 332MODULE_DEVICE_TABLE(of, db7430_match); 333 334static struct spi_driver db7430_driver = { 335 .probe = db7430_probe, 336 .remove = db7430_remove, 337 .driver = { 338 .name = "db7430-panel", 339 .of_match_table = db7430_match, 340 }, 341}; 342module_spi_driver(db7430_driver); 343 344MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 345MODULE_DESCRIPTION("Samsung DB7430 panel driver"); 346MODULE_LICENSE("GPL v2");