cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-samsung-s6d27a1.c (8259B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Panel driver for the Samsung S6D27A1 480x800 DPI RGB panel.
      4 * Found in the Samsung Galaxy Ace 2 GT-I8160 mobile phone.
      5 */
      6
      7#include <drm/drm_mipi_dbi.h>
      8#include <drm/drm_modes.h>
      9#include <drm/drm_panel.h>
     10
     11#include <linux/delay.h>
     12#include <linux/gpio/consumer.h>
     13#include <linux/init.h>
     14#include <linux/kernel.h>
     15#include <linux/media-bus-format.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/regulator/consumer.h>
     19#include <linux/spi/spi.h>
     20
     21#include <video/mipi_display.h>
     22
     23#define S6D27A1_PASSWD_L2	0xF0	/* Password Command for Level 2 Control */
     24#define S6D27A1_RESCTL		0xB3	/* Resolution Select Control */
     25#define S6D27A1_PANELCTL2	0xB4	/* ASG Signal Control */
     26#define S6D27A1_READID1		0xDA	/* Read panel ID 1 */
     27#define S6D27A1_READID2		0xDB	/* Read panel ID 2 */
     28#define S6D27A1_READID3		0xDC	/* Read panel ID 3 */
     29#define S6D27A1_DISPCTL		0xF2	/* Display Control */
     30#define S6D27A1_MANPWR		0xF3	/* Manual Control */
     31#define S6D27A1_PWRCTL1		0xF4	/* Power Control */
     32#define S6D27A1_SRCCTL		0xF6	/* Source Control */
     33#define S6D27A1_PANELCTL	0xF7	/* Panel Control*/
     34
     35static const u8 s6d27a1_dbi_read_commands[] = {
     36	S6D27A1_READID1,
     37	S6D27A1_READID2,
     38	S6D27A1_READID3,
     39	0, /* sentinel */
     40};
     41
     42struct s6d27a1 {
     43	struct device *dev;
     44	struct mipi_dbi dbi;
     45	struct drm_panel panel;
     46	struct gpio_desc *reset;
     47	struct regulator_bulk_data regulators[2];
     48};
     49
     50static const struct drm_display_mode s6d27a1_480_800_mode = {
     51	/*
     52	 * The vendor driver states that the S6D27A1 panel
     53	 * has a pixel clock frequency of 49920000 Hz / 2 = 24960000 Hz.
     54	 */
     55	.clock = 24960,
     56	.hdisplay = 480,
     57	.hsync_start = 480 + 63,
     58	.hsync_end = 480 + 63 + 2,
     59	.htotal = 480 + 63 + 2 + 63,
     60	.vdisplay = 800,
     61	.vsync_start = 800 + 11,
     62	.vsync_end = 800 + 11 + 2,
     63	.vtotal = 800 + 11 + 2 + 10,
     64	.width_mm = 50,
     65	.height_mm = 84,
     66	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
     67};
     68
     69static inline struct s6d27a1 *to_s6d27a1(struct drm_panel *panel)
     70{
     71	return container_of(panel, struct s6d27a1, panel);
     72}
     73
     74static void s6d27a1_read_mtp_id(struct s6d27a1 *ctx)
     75{
     76	struct mipi_dbi *dbi = &ctx->dbi;
     77	u8 id1, id2, id3;
     78	int ret;
     79
     80	ret = mipi_dbi_command_read(dbi, S6D27A1_READID1, &id1);
     81	if (ret) {
     82		dev_err(ctx->dev, "unable to read MTP ID 1\n");
     83		return;
     84	}
     85	ret = mipi_dbi_command_read(dbi, S6D27A1_READID2, &id2);
     86	if (ret) {
     87		dev_err(ctx->dev, "unable to read MTP ID 2\n");
     88		return;
     89	}
     90	ret = mipi_dbi_command_read(dbi, S6D27A1_READID3, &id3);
     91	if (ret) {
     92		dev_err(ctx->dev, "unable to read MTP ID 3\n");
     93		return;
     94	}
     95	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
     96}
     97
     98static int s6d27a1_power_on(struct s6d27a1 *ctx)
     99{
    100	struct mipi_dbi *dbi = &ctx->dbi;
    101	int ret;
    102
    103	/* Power up */
    104	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->regulators),
    105				    ctx->regulators);
    106	if (ret) {
    107		dev_err(ctx->dev, "failed to enable regulators: %d\n", ret);
    108		return ret;
    109	}
    110
    111	msleep(20);
    112
    113	/* Assert reset >=1 ms */
    114	gpiod_set_value_cansleep(ctx->reset, 1);
    115	usleep_range(1000, 5000);
    116	/* De-assert reset */
    117	gpiod_set_value_cansleep(ctx->reset, 0);
    118	/* Wait >= 10 ms */
    119	msleep(20);
    120
    121	/*
    122	 * Exit sleep mode and initialize display - some hammering is
    123	 * necessary.
    124	 */
    125	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
    126	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
    127	msleep(120);
    128
    129	/* Magic to unlock level 2 control of the display */
    130	mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0x5A, 0x5A);
    131
    132	/* Configure resolution to 480RGBx800 */
    133	mipi_dbi_command(dbi, S6D27A1_RESCTL, 0x22);
    134
    135	mipi_dbi_command(dbi, S6D27A1_PANELCTL2, 0x00, 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x0c);
    136
    137	mipi_dbi_command(dbi, S6D27A1_MANPWR, 0x01, 0x00, 0x00, 0x08, 0x08, 0x02, 0x00);
    138
    139	mipi_dbi_command(dbi, S6D27A1_DISPCTL, 0x19, 0x00, 0x08, 0x0D, 0x03, 0x41, 0x3F);
    140
    141	mipi_dbi_command(dbi, S6D27A1_PWRCTL1, 0x00, 0x00, 0x00, 0x00, 0x55,
    142					0x44, 0x05, 0x88, 0x4B, 0x50);
    143
    144	mipi_dbi_command(dbi, S6D27A1_SRCCTL, 0x03, 0x09, 0x8A, 0x00, 0x01, 0x16);
    145
    146	mipi_dbi_command(dbi, S6D27A1_PANELCTL, 0x00, 0x05, 0x06, 0x07, 0x08,
    147					0x01, 0x09, 0x0D, 0x0A, 0x0E,
    148					0x0B, 0x0F, 0x0C, 0x10, 0x01,
    149					0x11, 0x12, 0x13, 0x14, 0x05,
    150					0x06, 0x07, 0x08, 0x01, 0x09,
    151					0x0D, 0x0A, 0x0E, 0x0B, 0x0F,
    152					0x0C, 0x10, 0x01, 0x11, 0x12,
    153					0x13, 0x14);
    154
    155	/* lock the level 2 control */
    156	mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0xA5, 0xA5);
    157
    158	s6d27a1_read_mtp_id(ctx);
    159
    160	return 0;
    161}
    162
    163static int s6d27a1_power_off(struct s6d27a1 *ctx)
    164{
    165	/* Go into RESET and disable regulators */
    166	gpiod_set_value_cansleep(ctx->reset, 1);
    167	return regulator_bulk_disable(ARRAY_SIZE(ctx->regulators),
    168				      ctx->regulators);
    169}
    170
    171static int s6d27a1_unprepare(struct drm_panel *panel)
    172{
    173	struct s6d27a1 *ctx = to_s6d27a1(panel);
    174	struct mipi_dbi *dbi = &ctx->dbi;
    175
    176	mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
    177	msleep(120);
    178	return s6d27a1_power_off(to_s6d27a1(panel));
    179}
    180
    181static int s6d27a1_disable(struct drm_panel *panel)
    182{
    183	struct s6d27a1 *ctx = to_s6d27a1(panel);
    184	struct mipi_dbi *dbi = &ctx->dbi;
    185
    186	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
    187	msleep(25);
    188
    189	return 0;
    190}
    191
    192static int s6d27a1_prepare(struct drm_panel *panel)
    193{
    194	return s6d27a1_power_on(to_s6d27a1(panel));
    195}
    196
    197static int s6d27a1_enable(struct drm_panel *panel)
    198{
    199	struct s6d27a1 *ctx = to_s6d27a1(panel);
    200	struct mipi_dbi *dbi = &ctx->dbi;
    201
    202	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
    203
    204	return 0;
    205}
    206
    207static int s6d27a1_get_modes(struct drm_panel *panel,
    208			    struct drm_connector *connector)
    209{
    210	struct s6d27a1 *ctx = to_s6d27a1(panel);
    211	struct drm_display_mode *mode;
    212	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
    213
    214	mode = drm_mode_duplicate(connector->dev, &s6d27a1_480_800_mode);
    215	if (!mode) {
    216		dev_err(ctx->dev, "failed to add mode\n");
    217		return -ENOMEM;
    218	}
    219
    220	connector->display_info.bpc = 8;
    221	connector->display_info.width_mm = mode->width_mm;
    222	connector->display_info.height_mm = mode->height_mm;
    223	connector->display_info.bus_flags =
    224		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
    225	drm_display_info_set_bus_formats(&connector->display_info,
    226					 &bus_format, 1);
    227
    228	drm_mode_set_name(mode);
    229	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
    230
    231	drm_mode_probed_add(connector, mode);
    232
    233	return 1;
    234}
    235
    236static const struct drm_panel_funcs s6d27a1_drm_funcs = {
    237	.disable = s6d27a1_disable,
    238	.unprepare = s6d27a1_unprepare,
    239	.prepare = s6d27a1_prepare,
    240	.enable = s6d27a1_enable,
    241	.get_modes = s6d27a1_get_modes,
    242};
    243
    244static int s6d27a1_probe(struct spi_device *spi)
    245{
    246	struct device *dev = &spi->dev;
    247	struct s6d27a1 *ctx;
    248	int ret;
    249
    250	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
    251	if (!ctx)
    252		return -ENOMEM;
    253
    254	ctx->dev = dev;
    255
    256	/*
    257	 * VCI   is the analog voltage supply
    258	 * VCCIO is the digital I/O voltage supply
    259	 */
    260	ctx->regulators[0].supply = "vci";
    261	ctx->regulators[1].supply = "vccio";
    262	ret = devm_regulator_bulk_get(dev,
    263				      ARRAY_SIZE(ctx->regulators),
    264				      ctx->regulators);
    265	if (ret)
    266		return dev_err_probe(dev, ret, "failed to get regulators\n");
    267
    268	ctx->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
    269	if (IS_ERR(ctx->reset)) {
    270		ret = PTR_ERR(ctx->reset);
    271		return dev_err_probe(dev, ret, "no RESET GPIO\n");
    272	}
    273
    274	ret = mipi_dbi_spi_init(spi, &ctx->dbi, NULL);
    275	if (ret)
    276		return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
    277
    278	ctx->dbi.read_commands = s6d27a1_dbi_read_commands;
    279
    280	drm_panel_init(&ctx->panel, dev, &s6d27a1_drm_funcs,
    281		       DRM_MODE_CONNECTOR_DPI);
    282
    283	ret = drm_panel_of_backlight(&ctx->panel);
    284	if (ret)
    285		return dev_err_probe(dev, ret, "failed to add backlight\n");
    286
    287	spi_set_drvdata(spi, ctx);
    288
    289	drm_panel_add(&ctx->panel);
    290
    291	return 0;
    292}
    293
    294static void s6d27a1_remove(struct spi_device *spi)
    295{
    296	struct s6d27a1 *ctx = spi_get_drvdata(spi);
    297
    298	drm_panel_remove(&ctx->panel);
    299}
    300
    301static const struct of_device_id s6d27a1_match[] = {
    302	{ .compatible = "samsung,s6d27a1", },
    303	{ /* sentinel */ },
    304};
    305MODULE_DEVICE_TABLE(of, s6d27a1_match);
    306
    307static struct spi_driver s6d27a1_driver = {
    308	.probe		= s6d27a1_probe,
    309	.remove		= s6d27a1_remove,
    310	.driver		= {
    311		.name	= "s6d27a1-panel",
    312		.of_match_table = s6d27a1_match,
    313	},
    314};
    315module_spi_driver(s6d27a1_driver);
    316
    317MODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>");
    318MODULE_DESCRIPTION("Samsung S6D27A1 panel driver");
    319MODULE_LICENSE("GPL v2");