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-boe-bf060y8m-aj0.c (11970B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * BOE BF060Y8M-AJ0 5.99" MIPI-DSI OLED Panel on SW43404 DriverIC
      4 *
      5 * Copyright (c) 2020 AngeloGioacchino Del Regno
      6 *                    <angelogioacchino.delregno@somainline.org>
      7 */
      8
      9#include <linux/backlight.h>
     10#include <linux/delay.h>
     11#include <linux/gpio/consumer.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/regulator/consumer.h>
     15#include <video/mipi_display.h>
     16#include <drm/drm_mipi_dsi.h>
     17#include <drm/drm_modes.h>
     18#include <drm/drm_panel.h>
     19
     20#define DCS_ALLOW_HBM_RANGE		0x0c
     21#define DCS_DISALLOW_HBM_RANGE		0x08
     22
     23enum boe_bf060y8m_aj0_supplies {
     24	BF060Y8M_VREG_VCC,
     25	BF060Y8M_VREG_VDDIO,
     26	BF060Y8M_VREG_VCI,
     27	BF060Y8M_VREG_EL_VDD,
     28	BF060Y8M_VREG_EL_VSS,
     29	BF060Y8M_VREG_MAX
     30};
     31
     32struct boe_bf060y8m_aj0 {
     33	struct drm_panel panel;
     34	struct mipi_dsi_device *dsi;
     35	struct regulator_bulk_data vregs[BF060Y8M_VREG_MAX];
     36	struct gpio_desc *reset_gpio;
     37	bool prepared;
     38};
     39
     40static inline
     41struct boe_bf060y8m_aj0 *to_boe_bf060y8m_aj0(struct drm_panel *panel)
     42{
     43	return container_of(panel, struct boe_bf060y8m_aj0, panel);
     44}
     45
     46#define dsi_dcs_write_seq(dsi, seq...) do {				\
     47		static const u8 d[] = { seq };				\
     48		int ret;						\
     49		ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d));	\
     50		if (ret < 0)						\
     51			return ret;					\
     52	} while (0)
     53
     54static void boe_bf060y8m_aj0_reset(struct boe_bf060y8m_aj0 *boe)
     55{
     56	gpiod_set_value_cansleep(boe->reset_gpio, 0);
     57	usleep_range(2000, 3000);
     58	gpiod_set_value_cansleep(boe->reset_gpio, 1);
     59	usleep_range(15000, 16000);
     60	gpiod_set_value_cansleep(boe->reset_gpio, 0);
     61	usleep_range(5000, 6000);
     62}
     63
     64static int boe_bf060y8m_aj0_on(struct boe_bf060y8m_aj0 *boe)
     65{
     66	struct mipi_dsi_device *dsi = boe->dsi;
     67	struct device *dev = &dsi->dev;
     68	int ret;
     69
     70	dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
     71	dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x4c);
     72	dsi_dcs_write_seq(dsi, MIPI_DCS_SET_3D_CONTROL, 0x10);
     73	dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, DCS_ALLOW_HBM_RANGE);
     74	dsi_dcs_write_seq(dsi, 0xf8,
     75			  0x00, 0x08, 0x10, 0x00, 0x22, 0x00, 0x00, 0x2d);
     76
     77	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
     78	if (ret < 0) {
     79		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
     80		return ret;
     81	}
     82	msleep(30);
     83
     84	dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
     85	dsi_dcs_write_seq(dsi, 0xc0,
     86			  0x08, 0x48, 0x65, 0x33, 0x33, 0x33,
     87			  0x2a, 0x31, 0x39, 0x20, 0x09);
     88	dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x00, 0x00, 0x1f, 0x1f,
     89			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
     90			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
     91	dsi_dcs_write_seq(dsi, 0xe2, 0x20, 0x04, 0x10, 0x12, 0x92,
     92			  0x4f, 0x8f, 0x44, 0x84, 0x83, 0x83, 0x83,
     93			  0x5c, 0x5c, 0x5c);
     94	dsi_dcs_write_seq(dsi, 0xde, 0x01, 0x2c, 0x00, 0x77, 0x3e);
     95
     96	msleep(30);
     97
     98	ret = mipi_dsi_dcs_set_display_on(dsi);
     99	if (ret < 0) {
    100		dev_err(dev, "Failed to set display on: %d\n", ret);
    101		return ret;
    102	}
    103	msleep(50);
    104
    105	return 0;
    106}
    107
    108static int boe_bf060y8m_aj0_off(struct boe_bf060y8m_aj0 *boe)
    109{
    110	struct mipi_dsi_device *dsi = boe->dsi;
    111	struct device *dev = &dsi->dev;
    112	int ret;
    113
    114	/* OFF commands sent in HS mode */
    115	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
    116	ret = mipi_dsi_dcs_set_display_off(dsi);
    117	if (ret < 0) {
    118		dev_err(dev, "Failed to set display off: %d\n", ret);
    119		return ret;
    120	}
    121	msleep(20);
    122
    123	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
    124	if (ret < 0) {
    125		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
    126		return ret;
    127	}
    128	usleep_range(1000, 2000);
    129	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
    130
    131	return 0;
    132}
    133
    134static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel)
    135{
    136	struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
    137	struct device *dev = &boe->dsi->dev;
    138	int ret;
    139
    140	if (boe->prepared)
    141		return 0;
    142
    143	/*
    144	 * Enable EL Driving Voltage first - doing that at the beginning
    145	 * or at the end of the power sequence doesn't matter, so enable
    146	 * it here to avoid yet another usleep at the end.
    147	 */
    148	ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
    149	if (ret)
    150		return ret;
    151	ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
    152	if (ret)
    153		goto err_elvss;
    154
    155	ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
    156	if (ret)
    157		goto err_vcc;
    158	usleep_range(1000, 2000);
    159	ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
    160	if (ret)
    161		goto err_vddio;
    162	usleep_range(500, 1000);
    163	ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCI].consumer);
    164	if (ret)
    165		goto err_vci;
    166	usleep_range(2000, 3000);
    167
    168	boe_bf060y8m_aj0_reset(boe);
    169
    170	ret = boe_bf060y8m_aj0_on(boe);
    171	if (ret < 0) {
    172		dev_err(dev, "Failed to initialize panel: %d\n", ret);
    173		gpiod_set_value_cansleep(boe->reset_gpio, 1);
    174		return ret;
    175	}
    176
    177	boe->prepared = true;
    178	return 0;
    179
    180err_vci:
    181	regulator_disable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
    182err_vddio:
    183	regulator_disable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
    184err_vcc:
    185	regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
    186err_elvss:
    187	regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
    188	return ret;
    189}
    190
    191static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel)
    192{
    193	struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
    194	struct device *dev = &boe->dsi->dev;
    195	int ret;
    196
    197	if (!boe->prepared)
    198		return 0;
    199
    200	ret = boe_bf060y8m_aj0_off(boe);
    201	if (ret < 0)
    202		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
    203
    204	gpiod_set_value_cansleep(boe->reset_gpio, 1);
    205	ret = regulator_bulk_disable(ARRAY_SIZE(boe->vregs), boe->vregs);
    206
    207	boe->prepared = false;
    208	return 0;
    209}
    210
    211static const struct drm_display_mode boe_bf060y8m_aj0_mode = {
    212	.clock = 165268,
    213	.hdisplay = 1080,
    214	.hsync_start = 1080 + 36,
    215	.hsync_end = 1080 + 36 + 24,
    216	.htotal = 1080 + 36 + 24 + 96,
    217	.vdisplay = 2160,
    218	.vsync_start = 2160 + 16,
    219	.vsync_end = 2160 + 16 + 1,
    220	.vtotal = 2160 + 16 + 1 + 15,
    221	.width_mm = 68,   /* 68.04 mm */
    222	.height_mm = 136, /* 136.08 mm */
    223};
    224
    225static int boe_bf060y8m_aj0_get_modes(struct drm_panel *panel,
    226				      struct drm_connector *connector)
    227{
    228	struct drm_display_mode *mode;
    229
    230	mode = drm_mode_duplicate(connector->dev, &boe_bf060y8m_aj0_mode);
    231	if (!mode)
    232		return -ENOMEM;
    233
    234	drm_mode_set_name(mode);
    235
    236	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
    237	connector->display_info.width_mm = mode->width_mm;
    238	connector->display_info.height_mm = mode->height_mm;
    239	drm_mode_probed_add(connector, mode);
    240
    241	return 1;
    242}
    243
    244static const struct drm_panel_funcs boe_bf060y8m_aj0_panel_funcs = {
    245	.prepare = boe_bf060y8m_aj0_prepare,
    246	.unprepare = boe_bf060y8m_aj0_unprepare,
    247	.get_modes = boe_bf060y8m_aj0_get_modes,
    248};
    249
    250static int boe_bf060y8m_aj0_bl_update_status(struct backlight_device *bl)
    251{
    252	struct mipi_dsi_device *dsi = bl_get_data(bl);
    253	u16 brightness = backlight_get_brightness(bl);
    254	int ret;
    255
    256	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
    257	if (ret < 0)
    258		return ret;
    259
    260	return 0;
    261}
    262
    263static int boe_bf060y8m_aj0_bl_get_brightness(struct backlight_device *bl)
    264{
    265	struct mipi_dsi_device *dsi = bl_get_data(bl);
    266	u16 brightness;
    267	int ret;
    268
    269	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
    270	if (ret < 0)
    271		return ret;
    272
    273	return brightness & 0xff;
    274}
    275
    276static const struct backlight_ops boe_bf060y8m_aj0_bl_ops = {
    277	.update_status = boe_bf060y8m_aj0_bl_update_status,
    278	.get_brightness = boe_bf060y8m_aj0_bl_get_brightness,
    279};
    280
    281static struct backlight_device *
    282boe_bf060y8m_aj0_create_backlight(struct mipi_dsi_device *dsi)
    283{
    284	struct device *dev = &dsi->dev;
    285	const struct backlight_properties props = {
    286		.type = BACKLIGHT_RAW,
    287		.brightness = 127,
    288		.max_brightness = 255,
    289		.scale = BACKLIGHT_SCALE_NON_LINEAR,
    290	};
    291
    292	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
    293					      &boe_bf060y8m_aj0_bl_ops, &props);
    294}
    295
    296static int boe_bf060y8m_aj0_init_vregs(struct boe_bf060y8m_aj0 *boe,
    297				       struct device *dev)
    298{
    299	struct regulator *vreg;
    300	int ret;
    301
    302	boe->vregs[BF060Y8M_VREG_VCC].supply = "vcc";
    303	boe->vregs[BF060Y8M_VREG_VDDIO].supply = "vddio";
    304	boe->vregs[BF060Y8M_VREG_VCI].supply = "vci";
    305	boe->vregs[BF060Y8M_VREG_EL_VDD].supply = "elvdd";
    306	boe->vregs[BF060Y8M_VREG_EL_VSS].supply = "elvss";
    307	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(boe->vregs),
    308				      boe->vregs);
    309	if (ret < 0) {
    310		dev_err(dev, "Failed to get regulators: %d\n", ret);
    311		return ret;
    312	}
    313
    314	vreg = boe->vregs[BF060Y8M_VREG_VCC].consumer;
    315	ret = regulator_is_supported_voltage(vreg, 2700000, 3600000);
    316	if (!ret)
    317		return ret;
    318
    319	vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
    320	ret = regulator_is_supported_voltage(vreg, 1620000, 1980000);
    321	if (!ret)
    322		return ret;
    323
    324	vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
    325	ret = regulator_is_supported_voltage(vreg, 2600000, 3600000);
    326	if (!ret)
    327		return ret;
    328
    329	vreg = boe->vregs[BF060Y8M_VREG_EL_VDD].consumer;
    330	ret = regulator_is_supported_voltage(vreg, 4400000, 4800000);
    331	if (!ret)
    332		return ret;
    333
    334	/* ELVSS is negative: -5.00V to -1.40V */
    335	vreg = boe->vregs[BF060Y8M_VREG_EL_VSS].consumer;
    336	ret = regulator_is_supported_voltage(vreg, 1400000, 5000000);
    337	if (!ret)
    338		return ret;
    339
    340	/*
    341	 * Set min/max rated current, known only for VCI and VDDIO and,
    342	 * in case of failure, just go on gracefully, as this step is not
    343	 * guaranteed to succeed on all regulator HW but do a debug print
    344	 * to inform the developer during debugging.
    345	 * In any case, these two supplies are also optional, so they may
    346	 * be fixed-regulator which, at the time of writing, does not
    347	 * support fake current limiting.
    348	 */
    349	vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
    350	ret = regulator_set_current_limit(vreg, 1500, 2500);
    351	if (ret)
    352		dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
    353			boe->vregs[1].supply, ret);
    354
    355	vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
    356	ret = regulator_set_current_limit(vreg, 20000, 40000);
    357	if (ret)
    358		dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
    359			boe->vregs[2].supply, ret);
    360
    361	return 0;
    362}
    363
    364static int boe_bf060y8m_aj0_probe(struct mipi_dsi_device *dsi)
    365{
    366	struct device *dev = &dsi->dev;
    367	struct boe_bf060y8m_aj0 *boe;
    368	int ret;
    369
    370	boe = devm_kzalloc(dev, sizeof(*boe), GFP_KERNEL);
    371	if (!boe)
    372		return -ENOMEM;
    373
    374	ret = boe_bf060y8m_aj0_init_vregs(boe, dev);
    375	if (ret)
    376		return dev_err_probe(dev, ret,
    377				     "Failed to initialize supplies.\n");
    378
    379	boe->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
    380	if (IS_ERR(boe->reset_gpio))
    381		return dev_err_probe(dev, PTR_ERR(boe->reset_gpio),
    382				     "Failed to get reset-gpios\n");
    383
    384	boe->dsi = dsi;
    385	mipi_dsi_set_drvdata(dsi, boe);
    386
    387	dsi->lanes = 4;
    388	dsi->format = MIPI_DSI_FMT_RGB888;
    389	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
    390			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
    391			  MIPI_DSI_CLOCK_NON_CONTINUOUS |
    392			  MIPI_DSI_MODE_LPM;
    393
    394	drm_panel_init(&boe->panel, dev, &boe_bf060y8m_aj0_panel_funcs,
    395		       DRM_MODE_CONNECTOR_DSI);
    396
    397	boe->panel.backlight = boe_bf060y8m_aj0_create_backlight(dsi);
    398	if (IS_ERR(boe->panel.backlight))
    399		return dev_err_probe(dev, PTR_ERR(boe->panel.backlight),
    400				     "Failed to create backlight\n");
    401
    402	drm_panel_add(&boe->panel);
    403
    404	ret = mipi_dsi_attach(dsi);
    405	if (ret < 0) {
    406		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
    407		return ret;
    408	}
    409
    410	return 0;
    411}
    412
    413static int boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi)
    414{
    415	struct boe_bf060y8m_aj0 *boe = mipi_dsi_get_drvdata(dsi);
    416	int ret;
    417
    418	ret = mipi_dsi_detach(dsi);
    419	if (ret < 0)
    420		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
    421
    422	drm_panel_remove(&boe->panel);
    423
    424	return 0;
    425}
    426
    427static const struct of_device_id boe_bf060y8m_aj0_of_match[] = {
    428	{ .compatible = "boe,bf060y8m-aj0" },
    429	{ /* sentinel */ }
    430};
    431MODULE_DEVICE_TABLE(of, boe_bf060y8m_aj0_of_match);
    432
    433static struct mipi_dsi_driver boe_bf060y8m_aj0_driver = {
    434	.probe = boe_bf060y8m_aj0_probe,
    435	.remove = boe_bf060y8m_aj0_remove,
    436	.driver = {
    437		.name = "panel-sw43404-boe-fhd-amoled",
    438		.of_match_table = boe_bf060y8m_aj0_of_match,
    439	},
    440};
    441module_mipi_dsi_driver(boe_bf060y8m_aj0_driver);
    442
    443MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");
    444MODULE_DESCRIPTION("BOE BF060Y8M-AJ0 MIPI-DSI OLED panel");
    445MODULE_LICENSE("GPL v2");