18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Raydium RM67191 MIPI-DSI panel driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2019 NXP 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/backlight.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 168c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 178c2ecf20Sopenharmony_ci#include <video/videomode.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Panel specific color-format bits */ 248c2ecf20Sopenharmony_ci#define COL_FMT_16BPP 0x55 258c2ecf20Sopenharmony_ci#define COL_FMT_18BPP 0x66 268c2ecf20Sopenharmony_ci#define COL_FMT_24BPP 0x77 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Write Manufacture Command Set Control */ 298c2ecf20Sopenharmony_ci#define WRMAUCCTR 0xFE 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Manufacturer Command Set pages (CMD2) */ 328c2ecf20Sopenharmony_cistruct cmd_set_entry { 338c2ecf20Sopenharmony_ci u8 cmd; 348c2ecf20Sopenharmony_ci u8 param; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * There is no description in the Reference Manual about these commands. 398c2ecf20Sopenharmony_ci * We received them from vendor, so just use them as is. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic const struct cmd_set_entry manufacturer_cmd_set[] = { 428c2ecf20Sopenharmony_ci {0xFE, 0x0B}, 438c2ecf20Sopenharmony_ci {0x28, 0x40}, 448c2ecf20Sopenharmony_ci {0x29, 0x4F}, 458c2ecf20Sopenharmony_ci {0xFE, 0x0E}, 468c2ecf20Sopenharmony_ci {0x4B, 0x00}, 478c2ecf20Sopenharmony_ci {0x4C, 0x0F}, 488c2ecf20Sopenharmony_ci {0x4D, 0x20}, 498c2ecf20Sopenharmony_ci {0x4E, 0x40}, 508c2ecf20Sopenharmony_ci {0x4F, 0x60}, 518c2ecf20Sopenharmony_ci {0x50, 0xA0}, 528c2ecf20Sopenharmony_ci {0x51, 0xC0}, 538c2ecf20Sopenharmony_ci {0x52, 0xE0}, 548c2ecf20Sopenharmony_ci {0x53, 0xFF}, 558c2ecf20Sopenharmony_ci {0xFE, 0x0D}, 568c2ecf20Sopenharmony_ci {0x18, 0x08}, 578c2ecf20Sopenharmony_ci {0x42, 0x00}, 588c2ecf20Sopenharmony_ci {0x08, 0x41}, 598c2ecf20Sopenharmony_ci {0x46, 0x02}, 608c2ecf20Sopenharmony_ci {0x72, 0x09}, 618c2ecf20Sopenharmony_ci {0xFE, 0x0A}, 628c2ecf20Sopenharmony_ci {0x24, 0x17}, 638c2ecf20Sopenharmony_ci {0x04, 0x07}, 648c2ecf20Sopenharmony_ci {0x1A, 0x0C}, 658c2ecf20Sopenharmony_ci {0x0F, 0x44}, 668c2ecf20Sopenharmony_ci {0xFE, 0x04}, 678c2ecf20Sopenharmony_ci {0x00, 0x0C}, 688c2ecf20Sopenharmony_ci {0x05, 0x08}, 698c2ecf20Sopenharmony_ci {0x06, 0x08}, 708c2ecf20Sopenharmony_ci {0x08, 0x08}, 718c2ecf20Sopenharmony_ci {0x09, 0x08}, 728c2ecf20Sopenharmony_ci {0x0A, 0xE6}, 738c2ecf20Sopenharmony_ci {0x0B, 0x8C}, 748c2ecf20Sopenharmony_ci {0x1A, 0x12}, 758c2ecf20Sopenharmony_ci {0x1E, 0xE0}, 768c2ecf20Sopenharmony_ci {0x29, 0x93}, 778c2ecf20Sopenharmony_ci {0x2A, 0x93}, 788c2ecf20Sopenharmony_ci {0x2F, 0x02}, 798c2ecf20Sopenharmony_ci {0x31, 0x02}, 808c2ecf20Sopenharmony_ci {0x33, 0x05}, 818c2ecf20Sopenharmony_ci {0x37, 0x2D}, 828c2ecf20Sopenharmony_ci {0x38, 0x2D}, 838c2ecf20Sopenharmony_ci {0x3A, 0x1E}, 848c2ecf20Sopenharmony_ci {0x3B, 0x1E}, 858c2ecf20Sopenharmony_ci {0x3D, 0x27}, 868c2ecf20Sopenharmony_ci {0x3F, 0x80}, 878c2ecf20Sopenharmony_ci {0x40, 0x40}, 888c2ecf20Sopenharmony_ci {0x41, 0xE0}, 898c2ecf20Sopenharmony_ci {0x4F, 0x2F}, 908c2ecf20Sopenharmony_ci {0x50, 0x1E}, 918c2ecf20Sopenharmony_ci {0xFE, 0x06}, 928c2ecf20Sopenharmony_ci {0x00, 0xCC}, 938c2ecf20Sopenharmony_ci {0x05, 0x05}, 948c2ecf20Sopenharmony_ci {0x07, 0xA2}, 958c2ecf20Sopenharmony_ci {0x08, 0xCC}, 968c2ecf20Sopenharmony_ci {0x0D, 0x03}, 978c2ecf20Sopenharmony_ci {0x0F, 0xA2}, 988c2ecf20Sopenharmony_ci {0x32, 0xCC}, 998c2ecf20Sopenharmony_ci {0x37, 0x05}, 1008c2ecf20Sopenharmony_ci {0x39, 0x83}, 1018c2ecf20Sopenharmony_ci {0x3A, 0xCC}, 1028c2ecf20Sopenharmony_ci {0x41, 0x04}, 1038c2ecf20Sopenharmony_ci {0x43, 0x83}, 1048c2ecf20Sopenharmony_ci {0x44, 0xCC}, 1058c2ecf20Sopenharmony_ci {0x49, 0x05}, 1068c2ecf20Sopenharmony_ci {0x4B, 0xA2}, 1078c2ecf20Sopenharmony_ci {0x4C, 0xCC}, 1088c2ecf20Sopenharmony_ci {0x51, 0x03}, 1098c2ecf20Sopenharmony_ci {0x53, 0xA2}, 1108c2ecf20Sopenharmony_ci {0x75, 0xCC}, 1118c2ecf20Sopenharmony_ci {0x7A, 0x03}, 1128c2ecf20Sopenharmony_ci {0x7C, 0x83}, 1138c2ecf20Sopenharmony_ci {0x7D, 0xCC}, 1148c2ecf20Sopenharmony_ci {0x82, 0x02}, 1158c2ecf20Sopenharmony_ci {0x84, 0x83}, 1168c2ecf20Sopenharmony_ci {0x85, 0xEC}, 1178c2ecf20Sopenharmony_ci {0x86, 0x0F}, 1188c2ecf20Sopenharmony_ci {0x87, 0xFF}, 1198c2ecf20Sopenharmony_ci {0x88, 0x00}, 1208c2ecf20Sopenharmony_ci {0x8A, 0x02}, 1218c2ecf20Sopenharmony_ci {0x8C, 0xA2}, 1228c2ecf20Sopenharmony_ci {0x8D, 0xEA}, 1238c2ecf20Sopenharmony_ci {0x8E, 0x01}, 1248c2ecf20Sopenharmony_ci {0x8F, 0xE8}, 1258c2ecf20Sopenharmony_ci {0xFE, 0x06}, 1268c2ecf20Sopenharmony_ci {0x90, 0x0A}, 1278c2ecf20Sopenharmony_ci {0x92, 0x06}, 1288c2ecf20Sopenharmony_ci {0x93, 0xA0}, 1298c2ecf20Sopenharmony_ci {0x94, 0xA8}, 1308c2ecf20Sopenharmony_ci {0x95, 0xEC}, 1318c2ecf20Sopenharmony_ci {0x96, 0x0F}, 1328c2ecf20Sopenharmony_ci {0x97, 0xFF}, 1338c2ecf20Sopenharmony_ci {0x98, 0x00}, 1348c2ecf20Sopenharmony_ci {0x9A, 0x02}, 1358c2ecf20Sopenharmony_ci {0x9C, 0xA2}, 1368c2ecf20Sopenharmony_ci {0xAC, 0x04}, 1378c2ecf20Sopenharmony_ci {0xFE, 0x06}, 1388c2ecf20Sopenharmony_ci {0xB1, 0x12}, 1398c2ecf20Sopenharmony_ci {0xB2, 0x17}, 1408c2ecf20Sopenharmony_ci {0xB3, 0x17}, 1418c2ecf20Sopenharmony_ci {0xB4, 0x17}, 1428c2ecf20Sopenharmony_ci {0xB5, 0x17}, 1438c2ecf20Sopenharmony_ci {0xB6, 0x11}, 1448c2ecf20Sopenharmony_ci {0xB7, 0x08}, 1458c2ecf20Sopenharmony_ci {0xB8, 0x09}, 1468c2ecf20Sopenharmony_ci {0xB9, 0x06}, 1478c2ecf20Sopenharmony_ci {0xBA, 0x07}, 1488c2ecf20Sopenharmony_ci {0xBB, 0x17}, 1498c2ecf20Sopenharmony_ci {0xBC, 0x17}, 1508c2ecf20Sopenharmony_ci {0xBD, 0x17}, 1518c2ecf20Sopenharmony_ci {0xBE, 0x17}, 1528c2ecf20Sopenharmony_ci {0xBF, 0x17}, 1538c2ecf20Sopenharmony_ci {0xC0, 0x17}, 1548c2ecf20Sopenharmony_ci {0xC1, 0x17}, 1558c2ecf20Sopenharmony_ci {0xC2, 0x17}, 1568c2ecf20Sopenharmony_ci {0xC3, 0x17}, 1578c2ecf20Sopenharmony_ci {0xC4, 0x0F}, 1588c2ecf20Sopenharmony_ci {0xC5, 0x0E}, 1598c2ecf20Sopenharmony_ci {0xC6, 0x00}, 1608c2ecf20Sopenharmony_ci {0xC7, 0x01}, 1618c2ecf20Sopenharmony_ci {0xC8, 0x10}, 1628c2ecf20Sopenharmony_ci {0xFE, 0x06}, 1638c2ecf20Sopenharmony_ci {0x95, 0xEC}, 1648c2ecf20Sopenharmony_ci {0x8D, 0xEE}, 1658c2ecf20Sopenharmony_ci {0x44, 0xEC}, 1668c2ecf20Sopenharmony_ci {0x4C, 0xEC}, 1678c2ecf20Sopenharmony_ci {0x32, 0xEC}, 1688c2ecf20Sopenharmony_ci {0x3A, 0xEC}, 1698c2ecf20Sopenharmony_ci {0x7D, 0xEC}, 1708c2ecf20Sopenharmony_ci {0x75, 0xEC}, 1718c2ecf20Sopenharmony_ci {0x00, 0xEC}, 1728c2ecf20Sopenharmony_ci {0x08, 0xEC}, 1738c2ecf20Sopenharmony_ci {0x85, 0xEC}, 1748c2ecf20Sopenharmony_ci {0xA6, 0x21}, 1758c2ecf20Sopenharmony_ci {0xA7, 0x05}, 1768c2ecf20Sopenharmony_ci {0xA9, 0x06}, 1778c2ecf20Sopenharmony_ci {0x82, 0x06}, 1788c2ecf20Sopenharmony_ci {0x41, 0x06}, 1798c2ecf20Sopenharmony_ci {0x7A, 0x07}, 1808c2ecf20Sopenharmony_ci {0x37, 0x07}, 1818c2ecf20Sopenharmony_ci {0x05, 0x06}, 1828c2ecf20Sopenharmony_ci {0x49, 0x06}, 1838c2ecf20Sopenharmony_ci {0x0D, 0x04}, 1848c2ecf20Sopenharmony_ci {0x51, 0x04}, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const u32 rad_bus_formats[] = { 1888c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_RGB888_1X24, 1898c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_RGB666_1X18, 1908c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_RGB565_1X16, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic const u32 rad_bus_flags = DRM_BUS_FLAG_DE_LOW | 1948c2ecf20Sopenharmony_ci DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct rad_panel { 1978c2ecf20Sopenharmony_ci struct drm_panel panel; 1988c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci struct gpio_desc *reset; 2018c2ecf20Sopenharmony_ci struct backlight_device *backlight; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci struct regulator_bulk_data *supplies; 2048c2ecf20Sopenharmony_ci unsigned int num_supplies; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci bool prepared; 2078c2ecf20Sopenharmony_ci bool enabled; 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct drm_display_mode default_mode = { 2118c2ecf20Sopenharmony_ci .clock = 132000, 2128c2ecf20Sopenharmony_ci .hdisplay = 1080, 2138c2ecf20Sopenharmony_ci .hsync_start = 1080 + 20, 2148c2ecf20Sopenharmony_ci .hsync_end = 1080 + 20 + 2, 2158c2ecf20Sopenharmony_ci .htotal = 1080 + 20 + 2 + 34, 2168c2ecf20Sopenharmony_ci .vdisplay = 1920, 2178c2ecf20Sopenharmony_ci .vsync_start = 1920 + 10, 2188c2ecf20Sopenharmony_ci .vsync_end = 1920 + 10 + 2, 2198c2ecf20Sopenharmony_ci .vtotal = 1920 + 10 + 2 + 4, 2208c2ecf20Sopenharmony_ci .width_mm = 68, 2218c2ecf20Sopenharmony_ci .height_mm = 121, 2228c2ecf20Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | 2238c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NVSYNC, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic inline struct rad_panel *to_rad_panel(struct drm_panel *panel) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci return container_of(panel, struct rad_panel, panel); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int rad_panel_push_cmd_list(struct mipi_dsi_device *dsi) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci size_t i; 2348c2ecf20Sopenharmony_ci size_t count = ARRAY_SIZE(manufacturer_cmd_set); 2358c2ecf20Sopenharmony_ci int ret = 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2388c2ecf20Sopenharmony_ci const struct cmd_set_entry *entry = &manufacturer_cmd_set[i]; 2398c2ecf20Sopenharmony_ci u8 buffer[2] = { entry->cmd, entry->param }; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, &buffer, sizeof(buffer)); 2428c2ecf20Sopenharmony_ci if (ret < 0) 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int color_format_from_dsi_format(enum mipi_dsi_pixel_format format) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci switch (format) { 2528c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB565: 2538c2ecf20Sopenharmony_ci return COL_FMT_16BPP; 2548c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB666: 2558c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: 2568c2ecf20Sopenharmony_ci return COL_FMT_18BPP; 2578c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB888: 2588c2ecf20Sopenharmony_ci return COL_FMT_24BPP; 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci return COL_FMT_24BPP; /* for backward compatibility */ 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int rad_panel_prepare(struct drm_panel *panel) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct rad_panel *rad = to_rad_panel(panel); 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (rad->prepared) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(rad->num_supplies, rad->supplies); 2738c2ecf20Sopenharmony_ci if (ret) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (rad->reset) { 2778c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(rad->reset, 1); 2788c2ecf20Sopenharmony_ci usleep_range(3000, 5000); 2798c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(rad->reset, 0); 2808c2ecf20Sopenharmony_ci usleep_range(18000, 20000); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci rad->prepared = true; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int rad_panel_unprepare(struct drm_panel *panel) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct rad_panel *rad = to_rad_panel(panel); 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!rad->prepared) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * Right after asserting the reset, we need to release it, so that the 2988c2ecf20Sopenharmony_ci * touch driver can have an active connection with the touch controller 2998c2ecf20Sopenharmony_ci * even after the display is turned off. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci if (rad->reset) { 3028c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(rad->reset, 1); 3038c2ecf20Sopenharmony_ci usleep_range(15000, 17000); 3048c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(rad->reset, 0); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(rad->num_supplies, rad->supplies); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci rad->prepared = false; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int rad_panel_enable(struct drm_panel *panel) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct rad_panel *rad = to_rad_panel(panel); 3198c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = rad->dsi; 3208c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 3218c2ecf20Sopenharmony_ci int color_format = color_format_from_dsi_format(dsi->format); 3228c2ecf20Sopenharmony_ci int ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (rad->enabled) 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dsi->mode_flags |= MIPI_DSI_MODE_LPM; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = rad_panel_push_cmd_list(dsi); 3308c2ecf20Sopenharmony_ci if (ret < 0) { 3318c2ecf20Sopenharmony_ci dev_err(dev, "Failed to send MCS (%d)\n", ret); 3328c2ecf20Sopenharmony_ci goto fail; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Select User Command Set table (CMD1) */ 3368c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2); 3378c2ecf20Sopenharmony_ci if (ret < 0) 3388c2ecf20Sopenharmony_ci goto fail; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Software reset */ 3418c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_soft_reset(dsi); 3428c2ecf20Sopenharmony_ci if (ret < 0) { 3438c2ecf20Sopenharmony_ci dev_err(dev, "Failed to do Software Reset (%d)\n", ret); 3448c2ecf20Sopenharmony_ci goto fail; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci usleep_range(15000, 17000); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Set DSI mode */ 3508c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2); 3518c2ecf20Sopenharmony_ci if (ret < 0) { 3528c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set DSI mode (%d)\n", ret); 3538c2ecf20Sopenharmony_ci goto fail; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci /* Set tear ON */ 3568c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 3578c2ecf20Sopenharmony_ci if (ret < 0) { 3588c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set tear ON (%d)\n", ret); 3598c2ecf20Sopenharmony_ci goto fail; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci /* Set tear scanline */ 3628c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380); 3638c2ecf20Sopenharmony_ci if (ret < 0) { 3648c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set tear scanline (%d)\n", ret); 3658c2ecf20Sopenharmony_ci goto fail; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci /* Set pixel format */ 3688c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format); 3698c2ecf20Sopenharmony_ci dev_dbg(dev, "Interface color format set to 0x%x\n", color_format); 3708c2ecf20Sopenharmony_ci if (ret < 0) { 3718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set pixel format (%d)\n", ret); 3728c2ecf20Sopenharmony_ci goto fail; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci /* Exit sleep mode */ 3758c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 3768c2ecf20Sopenharmony_ci if (ret < 0) { 3778c2ecf20Sopenharmony_ci dev_err(dev, "Failed to exit sleep mode (%d)\n", ret); 3788c2ecf20Sopenharmony_ci goto fail; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci usleep_range(5000, 7000); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 3848c2ecf20Sopenharmony_ci if (ret < 0) { 3858c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set display ON (%d)\n", ret); 3868c2ecf20Sopenharmony_ci goto fail; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci backlight_enable(rad->backlight); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci rad->enabled = true; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cifail: 3968c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(rad->reset, 1); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int rad_panel_disable(struct drm_panel *panel) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct rad_panel *rad = to_rad_panel(panel); 4048c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = rad->dsi; 4058c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!rad->enabled) 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci dsi->mode_flags |= MIPI_DSI_MODE_LPM; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci backlight_disable(rad->backlight); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci usleep_range(10000, 12000); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 4188c2ecf20Sopenharmony_ci if (ret < 0) { 4198c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set display OFF (%d)\n", ret); 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 4268c2ecf20Sopenharmony_ci if (ret < 0) { 4278c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enter sleep mode (%d)\n", ret); 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci rad->enabled = false; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int rad_panel_get_modes(struct drm_panel *panel, 4378c2ecf20Sopenharmony_ci struct drm_connector *connector) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 4428c2ecf20Sopenharmony_ci if (!mode) { 4438c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 4448c2ecf20Sopenharmony_ci default_mode.hdisplay, default_mode.vdisplay, 4458c2ecf20Sopenharmony_ci drm_mode_vrefresh(&default_mode)); 4468c2ecf20Sopenharmony_ci return -ENOMEM; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 4508c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 4518c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 4548c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 4558c2ecf20Sopenharmony_ci connector->display_info.bus_flags = rad_bus_flags; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci drm_display_info_set_bus_formats(&connector->display_info, 4588c2ecf20Sopenharmony_ci rad_bus_formats, 4598c2ecf20Sopenharmony_ci ARRAY_SIZE(rad_bus_formats)); 4608c2ecf20Sopenharmony_ci return 1; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int rad_bl_get_brightness(struct backlight_device *bl) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = bl_get_data(bl); 4668c2ecf20Sopenharmony_ci struct rad_panel *rad = mipi_dsi_get_drvdata(dsi); 4678c2ecf20Sopenharmony_ci u16 brightness; 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!rad->prepared) 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); 4768c2ecf20Sopenharmony_ci if (ret < 0) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci bl->props.brightness = brightness; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return brightness & 0xff; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int rad_bl_update_status(struct backlight_device *bl) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = bl_get_data(bl); 4878c2ecf20Sopenharmony_ci struct rad_panel *rad = mipi_dsi_get_drvdata(dsi); 4888c2ecf20Sopenharmony_ci int ret = 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (!rad->prepared) 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); 4968c2ecf20Sopenharmony_ci if (ret < 0) 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic const struct backlight_ops rad_bl_ops = { 5038c2ecf20Sopenharmony_ci .update_status = rad_bl_update_status, 5048c2ecf20Sopenharmony_ci .get_brightness = rad_bl_get_brightness, 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs rad_panel_funcs = { 5088c2ecf20Sopenharmony_ci .prepare = rad_panel_prepare, 5098c2ecf20Sopenharmony_ci .unprepare = rad_panel_unprepare, 5108c2ecf20Sopenharmony_ci .enable = rad_panel_enable, 5118c2ecf20Sopenharmony_ci .disable = rad_panel_disable, 5128c2ecf20Sopenharmony_ci .get_modes = rad_panel_get_modes, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic const char * const rad_supply_names[] = { 5168c2ecf20Sopenharmony_ci "v3p3", 5178c2ecf20Sopenharmony_ci "v1p8", 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int rad_init_regulators(struct rad_panel *rad) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct device *dev = &rad->dsi->dev; 5238c2ecf20Sopenharmony_ci int i; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci rad->num_supplies = ARRAY_SIZE(rad_supply_names); 5268c2ecf20Sopenharmony_ci rad->supplies = devm_kcalloc(dev, rad->num_supplies, 5278c2ecf20Sopenharmony_ci sizeof(*rad->supplies), GFP_KERNEL); 5288c2ecf20Sopenharmony_ci if (!rad->supplies) 5298c2ecf20Sopenharmony_ci return -ENOMEM; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci for (i = 0; i < rad->num_supplies; i++) 5328c2ecf20Sopenharmony_ci rad->supplies[i].supply = rad_supply_names[i]; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return devm_regulator_bulk_get(dev, rad->num_supplies, rad->supplies); 5358c2ecf20Sopenharmony_ci}; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int rad_panel_probe(struct mipi_dsi_device *dsi) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 5408c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 5418c2ecf20Sopenharmony_ci struct rad_panel *panel; 5428c2ecf20Sopenharmony_ci struct backlight_properties bl_props; 5438c2ecf20Sopenharmony_ci int ret; 5448c2ecf20Sopenharmony_ci u32 video_mode; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL); 5478c2ecf20Sopenharmony_ci if (!panel) 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, panel); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci panel->dsi = dsi; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 5558c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "video-mode", &video_mode); 5588c2ecf20Sopenharmony_ci if (!ret) { 5598c2ecf20Sopenharmony_ci switch (video_mode) { 5608c2ecf20Sopenharmony_ci case 0: 5618c2ecf20Sopenharmony_ci /* burst mode */ 5628c2ecf20Sopenharmony_ci dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST; 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci case 1: 5658c2ecf20Sopenharmony_ci /* non-burst mode with sync event */ 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci case 2: 5688c2ecf20Sopenharmony_ci /* non-burst mode with sync pulse */ 5698c2ecf20Sopenharmony_ci dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci default: 5728c2ecf20Sopenharmony_ci dev_warn(dev, "invalid video mode %d\n", video_mode); 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes); 5788c2ecf20Sopenharmony_ci if (ret) { 5798c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret); 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci panel->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 5848c2ecf20Sopenharmony_ci if (IS_ERR(panel->reset)) 5858c2ecf20Sopenharmony_ci return PTR_ERR(panel->reset); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci memset(&bl_props, 0, sizeof(bl_props)); 5888c2ecf20Sopenharmony_ci bl_props.type = BACKLIGHT_RAW; 5898c2ecf20Sopenharmony_ci bl_props.brightness = 255; 5908c2ecf20Sopenharmony_ci bl_props.max_brightness = 255; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci panel->backlight = devm_backlight_device_register(dev, dev_name(dev), 5938c2ecf20Sopenharmony_ci dev, dsi, &rad_bl_ops, 5948c2ecf20Sopenharmony_ci &bl_props); 5958c2ecf20Sopenharmony_ci if (IS_ERR(panel->backlight)) { 5968c2ecf20Sopenharmony_ci ret = PTR_ERR(panel->backlight); 5978c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register backlight (%d)\n", ret); 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci ret = rad_init_regulators(panel); 6028c2ecf20Sopenharmony_ci if (ret) 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci drm_panel_init(&panel->panel, dev, &rad_panel_funcs, 6068c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 6078c2ecf20Sopenharmony_ci dev_set_drvdata(dev, panel); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci drm_panel_add(&panel->panel); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 6128c2ecf20Sopenharmony_ci if (ret) 6138c2ecf20Sopenharmony_ci drm_panel_remove(&panel->panel); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int rad_panel_remove(struct mipi_dsi_device *dsi) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct rad_panel *rad = mipi_dsi_get_drvdata(dsi); 6218c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 6228c2ecf20Sopenharmony_ci int ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ret = mipi_dsi_detach(dsi); 6258c2ecf20Sopenharmony_ci if (ret) 6268c2ecf20Sopenharmony_ci dev_err(dev, "Failed to detach from host (%d)\n", ret); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci drm_panel_remove(&rad->panel); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void rad_panel_shutdown(struct mipi_dsi_device *dsi) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct rad_panel *rad = mipi_dsi_get_drvdata(dsi); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rad_panel_disable(&rad->panel); 6388c2ecf20Sopenharmony_ci rad_panel_unprepare(&rad->panel); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic const struct of_device_id rad_of_match[] = { 6428c2ecf20Sopenharmony_ci { .compatible = "raydium,rm67191", }, 6438c2ecf20Sopenharmony_ci { /* sentinel */ } 6448c2ecf20Sopenharmony_ci}; 6458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rad_of_match); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver rad_panel_driver = { 6488c2ecf20Sopenharmony_ci .driver = { 6498c2ecf20Sopenharmony_ci .name = "panel-raydium-rm67191", 6508c2ecf20Sopenharmony_ci .of_match_table = rad_of_match, 6518c2ecf20Sopenharmony_ci }, 6528c2ecf20Sopenharmony_ci .probe = rad_panel_probe, 6538c2ecf20Sopenharmony_ci .remove = rad_panel_remove, 6548c2ecf20Sopenharmony_ci .shutdown = rad_panel_shutdown, 6558c2ecf20Sopenharmony_ci}; 6568c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(rad_panel_driver); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>"); 6598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI panel"); 6608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 661