/*
* MIPI-DSI based s6e8aa0 AMOLED LCD 5.3 inch panel driver.
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd
*
* Inki Dae, <inki.dae@samsung.com>
* Donghwa Lee, <dh09.lee@samsung.com>
* Joongmock Shin <jmock.shin@samsung.com>
* Eunchul Kim <chulspro.kim@samsung.com>
* Tomasz Figa <t.figa@samsung.com>
* Andrzej Hajda <a.hajda@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
#define LDI_MTP_LENGTH 24
#define GAMMA_LEVEL_NUM 25
#define GAMMA_TABLE_LEN 26
#define PANELCTL_SS_MASK (1 << 5)
#define PANELCTL_SS_1_800 (0 << 5)
#define PANELCTL_SS_800_1 (1 << 5)
#define PANELCTL_GTCON_MASK (7 << 2)
#define PANELCTL_GTCON_110 (6 << 2)
#define PANELCTL_GTCON_111 (7 << 2)
#define PANELCTL_CLK1_CON_MASK (7 << 3)
#define PANELCTL_CLK1_000 (0 << 3)
#define PANELCTL_CLK1_001 (1 << 3)
#define PANELCTL_CLK2_CON_MASK (7 << 0)
#define PANELCTL_CLK2_000 (0 << 0)
#define PANELCTL_CLK2_001 (1 << 0)
#define PANELCTL_INT1_CON_MASK (7 << 3)
#define PANELCTL_INT1_000 (0 << 3)
#define PANELCTL_INT1_001 (1 << 3)
#define PANELCTL_INT2_CON_MASK (7 << 0)
#define PANELCTL_INT2_000 (0 << 0)
#define PANELCTL_INT2_001 (1 << 0)
#define PANELCTL_BICTL_CON_MASK (7 << 3)
#define PANELCTL_BICTL_000 (0 << 3)
#define PANELCTL_BICTL_001 (1 << 3)
#define PANELCTL_BICTLB_CON_MASK (7 << 0)
#define PANELCTL_BICTLB_000 (0 << 0)
#define PANELCTL_BICTLB_001 (1 << 0)
#define PANELCTL_EM_CLK1_CON_MASK (7 << 3)
#define PANELCTL_EM_CLK1_110 (6 << 3)
#define PANELCTL_EM_CLK1_111 (7 << 3)
#define PANELCTL_EM_CLK1B_CON_MASK (7 << 0)
#define PANELCTL_EM_CLK1B_110 (6 << 0)
#define PANELCTL_EM_CLK1B_111 (7 << 0)
#define PANELCTL_EM_CLK2_CON_MASK (7 << 3)
#define PANELCTL_EM_CLK2_110 (6 << 3)
#define PANELCTL_EM_CLK2_111 (7 << 3)
#define PANELCTL_EM_CLK2B_CON_MASK (7 << 0)
#define PANELCTL_EM_CLK2B_110 (6 << 0)
#define PANELCTL_EM_CLK2B_111 (7 << 0)
#define PANELCTL_EM_INT1_CON_MASK (7 << 3)
#define PANELCTL_EM_INT1_000 (0 << 3)
#define PANELCTL_EM_INT1_001 (1 << 3)
#define PANELCTL_EM_INT2_CON_MASK (7 << 0)
#define PANELCTL_EM_INT2_000 (0 << 0)
#define PANELCTL_EM_INT2_001 (1 << 0)
#define AID_DISABLE (0x4)
#define AID_1 (0x5)
#define AID_2 (0x6)
#define AID_3 (0x7)
typedef u8 s6e8aa0_gamma_table[GAMMA_TABLE_LEN];
struct s6e8aa0_variant {
u8 version;
const s6e8aa0_gamma_table *gamma_tables;
};
struct s6e8aa0 {
struct device *dev;
struct drm_panel panel;
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpio;
u32 power_on_delay;
u32 reset_delay;
u32 init_delay;
bool flip_horizontal;
bool flip_vertical;
struct videomode vm;
u32 width_mm;
u32 height_mm;
u8 version;
u8 id;
const struct s6e8aa0_variant *variant;
int brightness;
/* This field is tested by functions directly accessing DSI bus before
* transfer, transfer is skipped if it is set. In case of transfer
* failure or unexpected response the field is set to error value.
* Such construct allows to eliminate many checks in higher level
* functions.
*/
int error;
};
#define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel)
static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
{
int ret = ctx->error;
ctx->error = 0;
return ret;
}
static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
int ret;
if (ctx->error < 0)
return;
ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len);
if (ret < 0) {
dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
data);
ctx->error = ret;
}
}
static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
int ret;
if (ctx->error < 0)
return ctx->error;
ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len);
if (ret < 0) {
dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd);
ctx->error = ret;
}
return ret;
}
#define s6e8aa0_dcs_write_seq(ctx, seq...) \
({\
const u8 d[] = { seq };\
BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too big for stack");\
s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\
})
#define s6e8aa0_dcs_write_seq_static(ctx, seq...) \
({\
static const u8 d[] = { seq };\
s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\
})
static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *ctx)
{
s6e8aa0_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
}
static void s6e8aa0_panel_cond_set_v142(struct s6e8aa0 *ctx)
{
static const u8 aids[] = {
0x04, 0x04, 0x04, 0x04, 0x04, 0x60, 0x80, 0xA0
};
u8 aid = aids[ctx->id >> 5