diff options
Diffstat (limited to 'drivers/gpu/drm/panel/panel-samsung-s6e63m0.c')
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e63m0.c | 154 |
1 files changed, 92 insertions, 62 deletions
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index 2cc772fdc456..3eee67e2d86a 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -16,25 +16,34 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/regulator/consumer.h> -#include <linux/spi/spi.h> #include <video/mipi_display.h> +#include "panel-samsung-s6e63m0.h" + /* Manufacturer Command Set */ #define MCS_ELVSS_ON 0xb1 #define MCS_MIECTL1 0xc0 #define MCS_BCMODE 0xc1 +#define MCS_ERROR_CHECK 0xd5 +#define MCS_READ_ID1 0xda +#define MCS_READ_ID2 0xdb +#define MCS_READ_ID3 0xdc +#define MCS_LEVEL_2_KEY 0xf0 +#define MCS_MTP_KEY 0xf1 #define MCS_DISCTL 0xf2 #define MCS_SRCCTL 0xf6 #define MCS_IFCTL 0xf7 #define MCS_PANELCTL 0xF8 #define MCS_PGAMMACTL 0xfa +#define S6E63M0_LCD_ID_VALUE_M2 0xA4 +#define S6E63M0_LCD_ID_VALUE_SM2 0xB4 +#define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6 + #define NUM_GAMMA_LEVELS 11 #define GAMMA_TABLE_COUNT 23 -#define DATA_MASK 0x100 - #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1) /* array of gamma tables for gamma value 2.2 */ @@ -87,8 +96,11 @@ static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = { struct s6e63m0 { struct device *dev; + int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val); + int (*dcs_write)(struct device *dev, const u8 *data, size_t len); struct drm_panel panel; struct backlight_device *bl_dev; + u8 lcd_type; struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; @@ -134,42 +146,20 @@ static int s6e63m0_clear_error(struct s6e63m0 *ctx) return ret; } -static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data) +static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data) { - struct spi_device *spi = to_spi_device(ctx->dev); - struct spi_transfer xfer = { - .len = 2, - .tx_buf = &data, - }; - struct spi_message msg; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); + if (ctx->error < 0) + return; - return spi_sync(spi, &msg); + ctx->error = ctx->dcs_read(ctx->dev, cmd, data); } static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) { - int ret = 0; - if (ctx->error < 0 || len == 0) return; - dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data); - ret = s6e63m0_spi_write_word(ctx, *data); - - while (!ret && --len) { - ++data; - ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK); - } - - if (ret) { - dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, (int)len, data); - ctx->error = ret; - } - - usleep_range(300, 310); + ctx->error = ctx->dcs_write(ctx->dev, data, len); } #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \ @@ -178,6 +168,43 @@ static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ }) +static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx) +{ + u8 id1, id2, id3; + int ret; + + s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1); + s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2); + s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3); + + ret = s6e63m0_clear_error(ctx); + if (ret) { + dev_err(ctx->dev, "error checking LCD type (%d)\n", ret); + ctx->lcd_type = 0x00; + return ret; + } + + dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3); + + /* We attempt to detect what panel is mounted on the controller */ + switch (id2) { + case S6E63M0_LCD_ID_VALUE_M2: + dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n"); + break; + case S6E63M0_LCD_ID_VALUE_SM2: + case S6E63M0_LCD_ID_VALUE_SM2_1: + dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n"); + break; + default: + dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2); + break; + } + + ctx->lcd_type = id2; + + return 0; +} + static void s6e63m0_init(struct s6e63m0 *ctx) { s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, @@ -249,8 +276,6 @@ static void s6e63m0_init(struct s6e63m0 *ctx) s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0b); - - s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); } static int s6e63m0_power_on(struct s6e63m0 *ctx) @@ -263,6 +288,9 @@ static int s6e63m0_power_on(struct s6e63m0 *ctx) msleep(25); + /* Be sure to send a reset pulse */ + gpiod_set_value(ctx->reset_gpio, 1); + msleep(5); gpiod_set_value(ctx->reset_gpio, 0); msleep(120); @@ -292,8 +320,10 @@ static int s6e63m0_disable(struct drm_panel *panel) backlight_disable(ctx->bl_dev); + s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); + msleep(10); s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); - msleep(200); + msleep(120); ctx->enabled = false; @@ -331,6 +361,15 @@ static int s6e63m0_prepare(struct drm_panel *panel) if (ret < 0) return ret; + /* Magic to unlock level 2 control of the display */ + s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a); + /* Magic to unlock MTP reading */ + s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a); + + ret = s6e63m0_check_lcd_type(ctx); + if (ret < 0) + return ret; + s6e63m0_init(ctx); ret = s6e63m0_clear_error(ctx); @@ -350,7 +389,15 @@ static int s6e63m0_enable(struct drm_panel *panel) if (ctx->enabled) return 0; + s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(120); s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); + msleep(10); + + s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK, + 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3, + 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20, + 0x0F, 0x00); backlight_enable(ctx->bl_dev); @@ -429,9 +476,11 @@ static int s6e63m0_backlight_register(struct s6e63m0 *ctx) return ret; } -static int s6e63m0_probe(struct spi_device *spi) +int s6e63m0_probe(struct device *dev, + int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val), + int (*dcs_write)(struct device *dev, const u8 *data, size_t len), + bool dsi_mode) { - struct device *dev = &spi->dev; struct s6e63m0 *ctx; int ret; @@ -439,7 +488,9 @@ static int s6e63m0_probe(struct spi_device *spi) if (!ctx) return -ENOMEM; - spi_set_drvdata(spi, ctx); + ctx->dcs_read = dcs_read; + ctx->dcs_write = dcs_write; + dev_set_drvdata(dev, ctx); ctx->dev = dev; ctx->enabled = false; @@ -460,15 +511,8 @@ static int s6e63m0_probe(struct spi_device *spi) return PTR_ERR(ctx->reset_gpio); } - spi->bits_per_word = 9; - spi->mode = SPI_MODE_3; - ret = spi_setup(spi); - if (ret < 0) { - dev_err(dev, "spi setup failed.\n"); - return ret; - } - drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs, + dsi_mode ? DRM_MODE_CONNECTOR_DSI : DRM_MODE_CONNECTOR_DPI); ret = s6e63m0_backlight_register(ctx); @@ -479,31 +523,17 @@ static int s6e63m0_probe(struct spi_device *spi) return 0; } +EXPORT_SYMBOL_GPL(s6e63m0_probe); -static int s6e63m0_remove(struct spi_device *spi) +int s6e63m0_remove(struct device *dev) { - struct s6e63m0 *ctx = spi_get_drvdata(spi); + struct s6e63m0 *ctx = dev_get_drvdata(dev); drm_panel_remove(&ctx->panel); return 0; } - -static const struct of_device_id s6e63m0_of_match[] = { - { .compatible = "samsung,s6e63m0" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, s6e63m0_of_match); - -static struct spi_driver s6e63m0_driver = { - .probe = s6e63m0_probe, - .remove = s6e63m0_remove, - .driver = { - .name = "panel-samsung-s6e63m0", - .of_match_table = s6e63m0_of_match, - }, -}; -module_spi_driver(s6e63m0_driver); +EXPORT_SYMBOL_GPL(s6e63m0_remove); MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); MODULE_DESCRIPTION("s6e63m0 LCD Driver"); |