162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 NVIDIA Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <video/mipi_display.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <drm/drm_crtc.h> 1562306a36Sopenharmony_ci#include <drm/drm_device.h> 1662306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 1762306a36Sopenharmony_ci#include <drm/drm_panel.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct sharp_panel { 2062306a36Sopenharmony_ci struct drm_panel base; 2162306a36Sopenharmony_ci /* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */ 2262306a36Sopenharmony_ci struct mipi_dsi_device *link1; 2362306a36Sopenharmony_ci struct mipi_dsi_device *link2; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci struct regulator *supply; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci bool prepared; 2862306a36Sopenharmony_ci bool enabled; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci const struct drm_display_mode *mode; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return container_of(panel, struct sharp_panel, base); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void sharp_wait_frames(struct sharp_panel *sharp, unsigned int frames) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci unsigned int refresh = drm_mode_vrefresh(sharp->mode); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (WARN_ON(frames > refresh)) 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci msleep(1000 / (refresh / frames)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci u8 payload[3] = { offset >> 8, offset & 0xff, value }; 5162306a36Sopenharmony_ci struct mipi_dsi_device *dsi = sharp->link1; 5262306a36Sopenharmony_ci ssize_t err; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci err = mipi_dsi_generic_write(dsi, payload, sizeof(payload)); 5562306a36Sopenharmony_ci if (err < 0) { 5662306a36Sopenharmony_ci dev_err(&dsi->dev, "failed to write %02x to %04x: %zd\n", 5762306a36Sopenharmony_ci value, offset, err); 5862306a36Sopenharmony_ci return err; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci err = mipi_dsi_dcs_nop(dsi); 6262306a36Sopenharmony_ci if (err < 0) { 6362306a36Sopenharmony_ci dev_err(&dsi->dev, "failed to send DCS nop: %zd\n", err); 6462306a36Sopenharmony_ci return err; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci usleep_range(10, 20); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic __maybe_unused int sharp_panel_read(struct sharp_panel *sharp, 7362306a36Sopenharmony_ci u16 offset, u8 *value) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci ssize_t err; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci cpu_to_be16s(&offset); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset), 8062306a36Sopenharmony_ci value, sizeof(*value)); 8162306a36Sopenharmony_ci if (err < 0) 8262306a36Sopenharmony_ci dev_err(&sharp->link1->dev, "failed to read from %04x: %zd\n", 8362306a36Sopenharmony_ci offset, err); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return err; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int sharp_panel_disable(struct drm_panel *panel) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct sharp_panel *sharp = to_sharp_panel(panel); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!sharp->enabled) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci sharp->enabled = false; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int sharp_panel_unprepare(struct drm_panel *panel) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct sharp_panel *sharp = to_sharp_panel(panel); 10362306a36Sopenharmony_ci int err; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!sharp->prepared) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci sharp_wait_frames(sharp, 4); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci err = mipi_dsi_dcs_set_display_off(sharp->link1); 11162306a36Sopenharmony_ci if (err < 0) 11262306a36Sopenharmony_ci dev_err(panel->dev, "failed to set display off: %d\n", err); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1); 11562306a36Sopenharmony_ci if (err < 0) 11662306a36Sopenharmony_ci dev_err(panel->dev, "failed to enter sleep mode: %d\n", err); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci msleep(120); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci regulator_disable(sharp->supply); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci sharp->prepared = false; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int sharp_setup_symmetrical_split(struct mipi_dsi_device *left, 12862306a36Sopenharmony_ci struct mipi_dsi_device *right, 12962306a36Sopenharmony_ci const struct drm_display_mode *mode) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1); 13462306a36Sopenharmony_ci if (err < 0) { 13562306a36Sopenharmony_ci dev_err(&left->dev, "failed to set column address: %d\n", err); 13662306a36Sopenharmony_ci return err; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1); 14062306a36Sopenharmony_ci if (err < 0) { 14162306a36Sopenharmony_ci dev_err(&left->dev, "failed to set page address: %d\n", err); 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2, 14662306a36Sopenharmony_ci mode->hdisplay - 1); 14762306a36Sopenharmony_ci if (err < 0) { 14862306a36Sopenharmony_ci dev_err(&right->dev, "failed to set column address: %d\n", err); 14962306a36Sopenharmony_ci return err; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1); 15362306a36Sopenharmony_ci if (err < 0) { 15462306a36Sopenharmony_ci dev_err(&right->dev, "failed to set page address: %d\n", err); 15562306a36Sopenharmony_ci return err; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int sharp_panel_prepare(struct drm_panel *panel) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct sharp_panel *sharp = to_sharp_panel(panel); 16462306a36Sopenharmony_ci u8 format = MIPI_DCS_PIXEL_FMT_24BIT; 16562306a36Sopenharmony_ci int err; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (sharp->prepared) 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci err = regulator_enable(sharp->supply); 17162306a36Sopenharmony_ci if (err < 0) 17262306a36Sopenharmony_ci return err; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* 17562306a36Sopenharmony_ci * According to the datasheet, the panel needs around 10 ms to fully 17662306a36Sopenharmony_ci * power up. At least another 120 ms is required before exiting sleep 17762306a36Sopenharmony_ci * mode to make sure the panel is ready. Throw in another 20 ms for 17862306a36Sopenharmony_ci * good measure. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci msleep(150); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1); 18362306a36Sopenharmony_ci if (err < 0) { 18462306a36Sopenharmony_ci dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 18562306a36Sopenharmony_ci goto poweroff; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * The MIPI DCS specification mandates this delay only between the 19062306a36Sopenharmony_ci * exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly 19162306a36Sopenharmony_ci * necessary here. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci msleep(120); 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* set left-right mode */ 19862306a36Sopenharmony_ci err = sharp_panel_write(sharp, 0x1000, 0x2a); 19962306a36Sopenharmony_ci if (err < 0) { 20062306a36Sopenharmony_ci dev_err(panel->dev, "failed to set left-right mode: %d\n", err); 20162306a36Sopenharmony_ci goto poweroff; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* enable command mode */ 20562306a36Sopenharmony_ci err = sharp_panel_write(sharp, 0x1001, 0x01); 20662306a36Sopenharmony_ci if (err < 0) { 20762306a36Sopenharmony_ci dev_err(panel->dev, "failed to enable command mode: %d\n", err); 20862306a36Sopenharmony_ci goto poweroff; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format); 21262306a36Sopenharmony_ci if (err < 0) { 21362306a36Sopenharmony_ci dev_err(panel->dev, "failed to set pixel format: %d\n", err); 21462306a36Sopenharmony_ci goto poweroff; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * TODO: The device supports both left-right and even-odd split 21962306a36Sopenharmony_ci * configurations, but this driver currently supports only the left- 22062306a36Sopenharmony_ci * right split. To support a different mode a mechanism needs to be 22162306a36Sopenharmony_ci * put in place to communicate the configuration back to the DSI host 22262306a36Sopenharmony_ci * controller. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2, 22562306a36Sopenharmony_ci sharp->mode); 22662306a36Sopenharmony_ci if (err < 0) { 22762306a36Sopenharmony_ci dev_err(panel->dev, "failed to set up symmetrical split: %d\n", 22862306a36Sopenharmony_ci err); 22962306a36Sopenharmony_ci goto poweroff; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci err = mipi_dsi_dcs_set_display_on(sharp->link1); 23362306a36Sopenharmony_ci if (err < 0) { 23462306a36Sopenharmony_ci dev_err(panel->dev, "failed to set display on: %d\n", err); 23562306a36Sopenharmony_ci goto poweroff; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci sharp->prepared = true; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* wait for 6 frames before continuing */ 24162306a36Sopenharmony_ci sharp_wait_frames(sharp, 6); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cipoweroff: 24662306a36Sopenharmony_ci regulator_disable(sharp->supply); 24762306a36Sopenharmony_ci return err; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int sharp_panel_enable(struct drm_panel *panel) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct sharp_panel *sharp = to_sharp_panel(panel); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (sharp->enabled) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci sharp->enabled = true; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic const struct drm_display_mode default_mode = { 26362306a36Sopenharmony_ci .clock = 278000, 26462306a36Sopenharmony_ci .hdisplay = 2560, 26562306a36Sopenharmony_ci .hsync_start = 2560 + 128, 26662306a36Sopenharmony_ci .hsync_end = 2560 + 128 + 64, 26762306a36Sopenharmony_ci .htotal = 2560 + 128 + 64 + 64, 26862306a36Sopenharmony_ci .vdisplay = 1600, 26962306a36Sopenharmony_ci .vsync_start = 1600 + 4, 27062306a36Sopenharmony_ci .vsync_end = 1600 + 4 + 8, 27162306a36Sopenharmony_ci .vtotal = 1600 + 4 + 8 + 32, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int sharp_panel_get_modes(struct drm_panel *panel, 27562306a36Sopenharmony_ci struct drm_connector *connector) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct drm_display_mode *mode; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 28062306a36Sopenharmony_ci if (!mode) { 28162306a36Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", 28262306a36Sopenharmony_ci default_mode.hdisplay, default_mode.vdisplay, 28362306a36Sopenharmony_ci drm_mode_vrefresh(&default_mode)); 28462306a36Sopenharmony_ci return -ENOMEM; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci drm_mode_set_name(mode); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci connector->display_info.width_mm = 217; 29262306a36Sopenharmony_ci connector->display_info.height_mm = 136; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 1; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct drm_panel_funcs sharp_panel_funcs = { 29862306a36Sopenharmony_ci .disable = sharp_panel_disable, 29962306a36Sopenharmony_ci .unprepare = sharp_panel_unprepare, 30062306a36Sopenharmony_ci .prepare = sharp_panel_prepare, 30162306a36Sopenharmony_ci .enable = sharp_panel_enable, 30262306a36Sopenharmony_ci .get_modes = sharp_panel_get_modes, 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic const struct of_device_id sharp_of_match[] = { 30662306a36Sopenharmony_ci { .compatible = "sharp,lq101r1sx01", }, 30762306a36Sopenharmony_ci { } 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sharp_of_match); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int sharp_panel_add(struct sharp_panel *sharp) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci int ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci sharp->mode = &default_mode; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci sharp->supply = devm_regulator_get(&sharp->link1->dev, "power"); 31862306a36Sopenharmony_ci if (IS_ERR(sharp->supply)) 31962306a36Sopenharmony_ci return PTR_ERR(sharp->supply); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci drm_panel_init(&sharp->base, &sharp->link1->dev, &sharp_panel_funcs, 32262306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = drm_panel_of_backlight(&sharp->base); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci drm_panel_add(&sharp->base); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void sharp_panel_del(struct sharp_panel *sharp) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci if (sharp->base.dev) 33662306a36Sopenharmony_ci drm_panel_remove(&sharp->base); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (sharp->link2) 33962306a36Sopenharmony_ci put_device(&sharp->link2->dev); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int sharp_panel_probe(struct mipi_dsi_device *dsi) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct mipi_dsi_device *secondary = NULL; 34562306a36Sopenharmony_ci struct sharp_panel *sharp; 34662306a36Sopenharmony_ci struct device_node *np; 34762306a36Sopenharmony_ci int err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci dsi->lanes = 4; 35062306a36Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 35162306a36Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_LPM; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Find DSI-LINK1 */ 35462306a36Sopenharmony_ci np = of_parse_phandle(dsi->dev.of_node, "link2", 0); 35562306a36Sopenharmony_ci if (np) { 35662306a36Sopenharmony_ci secondary = of_find_mipi_dsi_device_by_node(np); 35762306a36Sopenharmony_ci of_node_put(np); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!secondary) 36062306a36Sopenharmony_ci return -EPROBE_DEFER; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* register a panel for only the DSI-LINK1 interface */ 36462306a36Sopenharmony_ci if (secondary) { 36562306a36Sopenharmony_ci sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL); 36662306a36Sopenharmony_ci if (!sharp) { 36762306a36Sopenharmony_ci put_device(&secondary->dev); 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mipi_dsi_set_drvdata(dsi, sharp); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci sharp->link2 = secondary; 37462306a36Sopenharmony_ci sharp->link1 = dsi; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci err = sharp_panel_add(sharp); 37762306a36Sopenharmony_ci if (err < 0) { 37862306a36Sopenharmony_ci put_device(&secondary->dev); 37962306a36Sopenharmony_ci return err; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci err = mipi_dsi_attach(dsi); 38462306a36Sopenharmony_ci if (err < 0) { 38562306a36Sopenharmony_ci if (secondary) 38662306a36Sopenharmony_ci sharp_panel_del(sharp); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void sharp_panel_remove(struct mipi_dsi_device *dsi) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); 39762306a36Sopenharmony_ci int err; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* only detach from host for the DSI-LINK2 interface */ 40062306a36Sopenharmony_ci if (!sharp) { 40162306a36Sopenharmony_ci mipi_dsi_detach(dsi); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci err = drm_panel_disable(&sharp->base); 40662306a36Sopenharmony_ci if (err < 0) 40762306a36Sopenharmony_ci dev_err(&dsi->dev, "failed to disable panel: %d\n", err); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err = mipi_dsi_detach(dsi); 41062306a36Sopenharmony_ci if (err < 0) 41162306a36Sopenharmony_ci dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci sharp_panel_del(sharp); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void sharp_panel_shutdown(struct mipi_dsi_device *dsi) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* nothing to do for DSI-LINK2 */ 42162306a36Sopenharmony_ci if (!sharp) 42262306a36Sopenharmony_ci return; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci drm_panel_disable(&sharp->base); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic struct mipi_dsi_driver sharp_panel_driver = { 42862306a36Sopenharmony_ci .driver = { 42962306a36Sopenharmony_ci .name = "panel-sharp-lq101r1sx01", 43062306a36Sopenharmony_ci .of_match_table = sharp_of_match, 43162306a36Sopenharmony_ci }, 43262306a36Sopenharmony_ci .probe = sharp_panel_probe, 43362306a36Sopenharmony_ci .remove = sharp_panel_remove, 43462306a36Sopenharmony_ci .shutdown = sharp_panel_shutdown, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_cimodule_mipi_dsi_driver(sharp_panel_driver); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 43962306a36Sopenharmony_ciMODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver"); 44062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 441