]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/panel: Rename Sony ACX424 to Novatek NT35560
authorLinus Walleij <linus.walleij@linaro.org>
Mon, 3 Jan 2022 11:38:20 +0000 (12:38 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Sun, 20 Feb 2022 23:29:19 +0000 (00:29 +0100)
A code drop from Sony Mobile reveals that the ACX424 panels are
built around the Novatek NT35560 panel controllers so just bite
the bullet and rename the driver and all basic symbols so that
we can modify this driver to cover any other panels also using
the Novatek NT35560 display controller.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220103113822.654592-1-linus.walleij@linaro.org
MAINTAINERS
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-novatek-nt35560.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-sony-acx424akp.c [deleted file]

index 8e6e892f99f066e4d053a83d4acad580d146489e..dd46d78feb949ded1e214e7a49f94853ae2dc786 100644 (file)
@@ -6127,6 +6127,13 @@ T:       git git://anongit.freedesktop.org/drm/drm-misc
 F:     Documentation/devicetree/bindings/display/panel/novatek,nt35510.yaml
 F:     drivers/gpu/drm/panel/panel-novatek-nt35510.c
 
+DRM DRIVER FOR NOVATEK NT35560 PANELS
+M:     Linus Walleij <linus.walleij@linaro.org>
+S:     Maintained
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+F:     Documentation/devicetree/bindings/display/panel/sony,acx424akp.yaml
+F:     drivers/gpu/drm/panel/panel-novatek-nt35560.c
+
 DRM DRIVER FOR NOVATEK NT36672A PANELS
 M:     Sumit Semwal <sumit.semwal@linaro.org>
 S:     Maintained
@@ -6258,12 +6265,6 @@ T:       git git://anongit.freedesktop.org/drm/drm-misc
 F:     Documentation/devicetree/bindings/display/sitronix,st7735r.yaml
 F:     drivers/gpu/drm/tiny/st7735r.c
 
-DRM DRIVER FOR SONY ACX424AKP PANELS
-M:     Linus Walleij <linus.walleij@linaro.org>
-S:     Maintained
-T:     git git://anongit.freedesktop.org/drm/drm-misc
-F:     drivers/gpu/drm/panel/panel-sony-acx424akp.c
-
 DRM DRIVER FOR ST-ERICSSON MCDE
 M:     Linus Walleij <linus.walleij@linaro.org>
 S:     Maintained
index 0aec5a10b064b4b93940615160b6a7fe727576a9..bb2e47229c6839f8ac4fb6f2db3e615d884d3498 100644 (file)
@@ -293,6 +293,18 @@ config DRM_PANEL_NOVATEK_NT35510
          around the Novatek NT35510 display controller, such as some
          Hydis panels.
 
+config DRM_PANEL_NOVATEK_NT35560
+       tristate "Novatek NT35560 DSI command mode panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       select VIDEOMODE_HELPERS
+       help
+         Say Y here if you want to enable the Novatek NT35560 display
+         controller. This panel supports DSI in both command and video
+         mode. This supports several panels such as Sony ACX424AKM and
+         ACX424AKP.
+
 config DRM_PANEL_NOVATEK_NT35950
        tristate "Novatek NT35950 DSI panel"
        depends on OF
@@ -593,17 +605,6 @@ config DRM_PANEL_SITRONIX_ST7789V
          Say Y here if you want to enable support for the Sitronix
          ST7789V controller for 240x320 LCD panels
 
-config DRM_PANEL_SONY_ACX424AKP
-       tristate "Sony ACX424AKP DSI command mode panel"
-       depends on OF
-       depends on DRM_MIPI_DSI
-       depends on BACKLIGHT_CLASS_DEVICE
-       select VIDEOMODE_HELPERS
-       help
-         Say Y here if you want to enable the Sony ACX424 display
-         panel. This panel supports DSI in both command and video
-         mode.
-
 config DRM_PANEL_SONY_ACX565AKM
        tristate "Sony ACX565AKM panel"
        depends on GPIOLIB && OF && SPI
index d99fbbce49d1766aedb7dfe6856f034a056d4cdb..5740911f637cd82cf37dc70b4cb992e40ab91492 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
@@ -60,7 +61,6 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS060T1SX01) += panel-sharp-ls060t1sx01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
-obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
 obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c
new file mode 100644 (file)
index 0000000..6208762
--- /dev/null
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MIPI-DSI Novatek NT35560-based panel controller.
+ *
+ * Supported panels include:
+ * Sony ACX424AKM - a 480x854 AMOLED DSI panel
+ * Sony ACX424AKP - a 480x864 AMOLED DSI panel
+ *
+ * Copyright (C) Linaro Ltd. 2019-2021
+ * Author: Linus Walleij
+ * Based on code and know-how from Marcus Lorentzon
+ * Copyright (C) ST-Ericsson SA 2010
+ */
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define NT35560_DCS_READ_ID1           0xDA
+#define NT35560_DCS_READ_ID2           0xDB
+#define NT35560_DCS_READ_ID3           0xDC
+#define NT35560_DCS_SET_MDDI           0xAE
+
+/*
+ * Sony seems to use vendor ID 0x81
+ */
+#define DISPLAY_SONY_ACX424AKP_ID1     0x811b
+#define DISPLAY_SONY_ACX424AKP_ID2     0x811a
+/*
+ * The third ID looks like a bug, vendor IDs begin at 0x80
+ * and panel 00 ... seems like default values.
+ */
+#define DISPLAY_SONY_ACX424AKP_ID3     0x8000
+
+struct nt35560 {
+       struct drm_panel panel;
+       struct device *dev;
+       struct regulator *supply;
+       struct gpio_desc *reset_gpio;
+       bool video_mode;
+};
+
+static const struct drm_display_mode sony_acx424akp_vid_mode = {
+       .clock = 27234,
+       .hdisplay = 480,
+       .hsync_start = 480 + 15,
+       .hsync_end = 480 + 15 + 0,
+       .htotal = 480 + 15 + 0 + 15,
+       .vdisplay = 864,
+       .vsync_start = 864 + 14,
+       .vsync_end = 864 + 14 + 1,
+       .vtotal = 864 + 14 + 1 + 11,
+       .width_mm = 48,
+       .height_mm = 84,
+       .flags = DRM_MODE_FLAG_PVSYNC,
+};
+
+/*
+ * The timings are not very helpful as the display is used in
+ * command mode using the maximum HS frequency.
+ */
+static const struct drm_display_mode sony_acx424akp_cmd_mode = {
+       .clock = 35478,
+       .hdisplay = 480,
+       .hsync_start = 480 + 154,
+       .hsync_end = 480 + 154 + 16,
+       .htotal = 480 + 154 + 16 + 32,
+       .vdisplay = 864,
+       .vsync_start = 864 + 1,
+       .vsync_end = 864 + 1 + 1,
+       .vtotal = 864 + 1 + 1 + 1,
+       /*
+        * Some desired refresh rate, experiments at the maximum "pixel"
+        * clock speed (HS clock 420 MHz) yields around 117Hz.
+        */
+       .width_mm = 48,
+       .height_mm = 84,
+};
+
+static inline struct nt35560 *panel_to_nt35560(struct drm_panel *panel)
+{
+       return container_of(panel, struct nt35560, panel);
+}
+
+#define FOSC                   20 /* 20Mhz */
+#define SCALE_FACTOR_NS_DIV_MHZ        1000
+
+static int nt35560_set_brightness(struct backlight_device *bl)
+{
+       struct nt35560 *nt = bl_get_data(bl);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+       int period_ns = 1023;
+       int duty_ns = bl->props.brightness;
+       u8 pwm_ratio;
+       u8 pwm_div;
+       u8 par;
+       int ret;
+
+       if (backlight_is_blank(bl)) {
+               /* Disable backlight */
+               par = 0x00;
+               ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+                                        &par, 1);
+               if (ret) {
+                       dev_err(nt->dev, "failed to disable display backlight (%d)\n", ret);
+                       return ret;
+               }
+               return 0;
+       }
+
+       /* Calculate the PWM duty cycle in n/256's */
+       pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1);
+       pwm_div = max(1,
+                     ((FOSC * period_ns) / 256) /
+                     SCALE_FACTOR_NS_DIV_MHZ);
+
+       /* Set up PWM dutycycle ONE byte (differs from the standard) */
+       dev_dbg(nt->dev, "calculated duty cycle %02x\n", pwm_ratio);
+       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+                                &pwm_ratio, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to set display PWM ratio (%d)\n", ret);
+               return ret;
+       }
+
+       /*
+        * Sequence to write PWMDIV:
+        *      address         data
+        *      0xF3            0xAA   CMD2 Unlock
+        *      0x00            0x01   Enter CMD2 page 0
+        *      0X7D            0x01   No reload MTP of CMD2 P1
+        *      0x22            PWMDIV
+        *      0x7F            0xAA   CMD2 page 1 lock
+        */
+       par = 0xaa;
+       ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to unlock CMD 2 (%d)\n", ret);
+               return ret;
+       }
+       par = 0x01;
+       ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to enter page 1 (%d)\n", ret);
+               return ret;
+       }
+       par = 0x01;
+       ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to disable MTP reload (%d)\n", ret);
+               return ret;
+       }
+       ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to set PWM divisor (%d)\n", ret);
+               return ret;
+       }
+       par = 0xaa;
+       ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to lock CMD 2 (%d)\n", ret);
+               return ret;
+       }
+
+       /* Enable backlight */
+       par = 0x24;
+       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+                                &par, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to enable display backlight (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct backlight_ops nt35560_bl_ops = {
+       .update_status = nt35560_set_brightness,
+};
+
+static const struct backlight_properties nt35560_bl_props = {
+       .type = BACKLIGHT_RAW,
+       .brightness = 512,
+       .max_brightness = 1023,
+};
+
+static int nt35560_read_id(struct nt35560 *nt)
+{
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+       u8 vendor, version, panel;
+       u16 val;
+       int ret;
+
+       ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID1, &vendor, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "could not vendor ID byte\n");
+               return ret;
+       }
+       ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID2, &version, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "could not read device version byte\n");
+               return ret;
+       }
+       ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID3, &panel, 1);
+       if (ret < 0) {
+               dev_err(nt->dev, "could not read panel ID byte\n");
+               return ret;
+       }
+
+       if (vendor == 0x00) {
+               dev_err(nt->dev, "device vendor ID is zero\n");
+               return -ENODEV;
+       }
+
+       val = (vendor << 8) | panel;
+       switch (val) {
+       case DISPLAY_SONY_ACX424AKP_ID1:
+       case DISPLAY_SONY_ACX424AKP_ID2:
+       case DISPLAY_SONY_ACX424AKP_ID3:
+               dev_info(nt->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n",
+                        vendor, version, panel);
+               break;
+       default:
+               dev_info(nt->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n",
+                        vendor, version, panel);
+               break;
+       }
+
+       return 0;
+}
+
+static int nt35560_power_on(struct nt35560 *nt)
+{
+       int ret;
+
+       ret = regulator_enable(nt->supply);
+       if (ret) {
+               dev_err(nt->dev, "failed to enable supply (%d)\n", ret);
+               return ret;
+       }
+
+       /* Assert RESET */
+       gpiod_set_value_cansleep(nt->reset_gpio, 1);
+       udelay(20);
+       /* De-assert RESET */
+       gpiod_set_value_cansleep(nt->reset_gpio, 0);
+       usleep_range(11000, 20000);
+
+       return 0;
+}
+
+static void nt35560_power_off(struct nt35560 *nt)
+{
+       /* Assert RESET */
+       gpiod_set_value_cansleep(nt->reset_gpio, 1);
+       usleep_range(11000, 20000);
+
+       regulator_disable(nt->supply);
+}
+
+static int nt35560_prepare(struct drm_panel *panel)
+{
+       struct nt35560 *nt = panel_to_nt35560(panel);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+       const u8 mddi = 3;
+       int ret;
+
+       ret = nt35560_power_on(nt);
+       if (ret)
+               return ret;
+
+       ret = nt35560_read_id(nt);
+       if (ret) {
+               dev_err(nt->dev, "failed to read panel ID (%d)\n", ret);
+               goto err_power_off;
+       }
+
+       /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
+       ret = mipi_dsi_dcs_set_tear_on(dsi,
+                                      MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+       if (ret) {
+               dev_err(nt->dev, "failed to enable vblank TE (%d)\n", ret);
+               goto err_power_off;
+       }
+
+       /*
+        * Set MDDI
+        *
+        * This presumably deactivates the Qualcomm MDDI interface and
+        * selects DSI, similar code is found in other drivers such as the
+        * Sharp LS043T1LE01 which makes us suspect that this panel may be
+        * using a Novatek NT35565 or similar display driver chip that shares
+        * this command. Due to the lack of documentation we cannot know for
+        * sure.
+        */
+       ret = mipi_dsi_dcs_write(dsi, NT35560_DCS_SET_MDDI,
+                                &mddi, sizeof(mddi));
+       if (ret < 0) {
+               dev_err(nt->dev, "failed to set MDDI (%d)\n", ret);
+               goto err_power_off;
+       }
+
+       /* Exit sleep mode */
+       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+       if (ret) {
+               dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret);
+               goto err_power_off;
+       }
+       msleep(140);
+
+       ret = mipi_dsi_dcs_set_display_on(dsi);
+       if (ret) {
+               dev_err(nt->dev, "failed to turn display on (%d)\n", ret);
+               goto err_power_off;
+       }
+       if (nt->video_mode) {
+               /* In video mode turn peripheral on */
+               ret = mipi_dsi_turn_on_peripheral(dsi);
+               if (ret) {
+                       dev_err(nt->dev, "failed to turn on peripheral\n");
+                       goto err_power_off;
+               }
+       }
+
+       return 0;
+
+err_power_off:
+       nt35560_power_off(nt);
+       return ret;
+}
+
+static int nt35560_unprepare(struct drm_panel *panel)
+{
+       struct nt35560 *nt = panel_to_nt35560(panel);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+       int ret;
+
+       ret = mipi_dsi_dcs_set_display_off(dsi);
+       if (ret) {
+               dev_err(nt->dev, "failed to turn display off (%d)\n", ret);
+               return ret;
+       }
+
+       /* Enter sleep mode */
+       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+       if (ret) {
+               dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret);
+               return ret;
+       }
+       msleep(85);
+
+       nt35560_power_off(nt);
+
+       return 0;
+}
+
+
+static int nt35560_get_modes(struct drm_panel *panel,
+                            struct drm_connector *connector)
+{
+       struct nt35560 *nt = panel_to_nt35560(panel);
+       struct drm_display_mode *mode;
+
+       if (nt->video_mode)
+               mode = drm_mode_duplicate(connector->dev,
+                                         &sony_acx424akp_vid_mode);
+       else
+               mode = drm_mode_duplicate(connector->dev,
+                                         &sony_acx424akp_cmd_mode);
+       if (!mode) {
+               dev_err(panel->dev, "bad mode or failed to add mode\n");
+               return -EINVAL;
+       }
+       drm_mode_set_name(mode);
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+       connector->display_info.width_mm = mode->width_mm;
+       connector->display_info.height_mm = mode->height_mm;
+
+       drm_mode_probed_add(connector, mode);
+
+       return 1; /* Number of modes */
+}
+
+static const struct drm_panel_funcs nt35560_drm_funcs = {
+       .unprepare = nt35560_unprepare,
+       .prepare = nt35560_prepare,
+       .get_modes = nt35560_get_modes,
+};
+
+static int nt35560_probe(struct mipi_dsi_device *dsi)
+{
+       struct device *dev = &dsi->dev;
+       struct nt35560 *nt;
+       int ret;
+
+       nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL);
+       if (!nt)
+               return -ENOMEM;
+       nt->video_mode = of_property_read_bool(dev->of_node,
+                                               "enforce-video-mode");
+
+       mipi_dsi_set_drvdata(dsi, nt);
+       nt->dev = dev;
+
+       dsi->lanes = 2;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       /*
+        * FIXME: these come from the ST-Ericsson vendor driver for the
+        * HREF520 and seems to reflect limitations in the PLLs on that
+        * platform, if you have the datasheet, please cross-check the
+        * actual max rates.
+        */
+       dsi->lp_rate = 19200000;
+       dsi->hs_rate = 420160000;
+
+       if (nt->video_mode)
+               /* Burst mode using event for sync */
+               dsi->mode_flags =
+                       MIPI_DSI_MODE_VIDEO |
+                       MIPI_DSI_MODE_VIDEO_BURST;
+       else
+               dsi->mode_flags =
+                       MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+       nt->supply = devm_regulator_get(dev, "vddi");
+       if (IS_ERR(nt->supply))
+               return PTR_ERR(nt->supply);
+
+       /* This asserts RESET by default */
+       nt->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+                                                GPIOD_OUT_HIGH);
+       if (IS_ERR(nt->reset_gpio))
+               return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),
+                                    "failed to request GPIO\n");
+
+       drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs,
+                      DRM_MODE_CONNECTOR_DSI);
+
+       nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt,
+                                       &nt35560_bl_ops, &nt35560_bl_props);
+       if (IS_ERR(nt->panel.backlight))
+               return dev_err_probe(dev, PTR_ERR(nt->panel.backlight),
+                                    "failed to register backlight device\n");
+
+       drm_panel_add(&nt->panel);
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0) {
+               drm_panel_remove(&nt->panel);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int nt35560_remove(struct mipi_dsi_device *dsi)
+{
+       struct nt35560 *nt = mipi_dsi_get_drvdata(dsi);
+
+       mipi_dsi_detach(dsi);
+       drm_panel_remove(&nt->panel);
+
+       return 0;
+}
+
+static const struct of_device_id nt35560_of_match[] = {
+       { .compatible = "sony,acx424akp" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nt35560_of_match);
+
+static struct mipi_dsi_driver nt35560_driver = {
+       .probe = nt35560_probe,
+       .remove = nt35560_remove,
+       .driver = {
+               .name = "panel-novatek-nt35560",
+               .of_match_table = nt35560_of_match,
+       },
+};
+module_mipi_dsi_driver(nt35560_driver);
+
+MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("MIPI-DSI Novatek NT35560 Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-sony-acx424akp.c b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
deleted file mode 100644 (file)
index 9536d56..0000000
+++ /dev/null
@@ -1,490 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * MIPI-DSI Sony ACX424AKP panel driver. This is a 480x864
- * AMOLED panel with a command-only DSI interface.
- *
- * Copyright (C) Linaro Ltd. 2019
- * Author: Linus Walleij
- * Based on code and know-how from Marcus Lorentzon
- * Copyright (C) ST-Ericsson SA 2010
- */
-#include <linux/backlight.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-
-#include <video/mipi_display.h>
-
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_panel.h>
-
-#define ACX424_DCS_READ_ID1            0xDA
-#define ACX424_DCS_READ_ID2            0xDB
-#define ACX424_DCS_READ_ID3            0xDC
-#define ACX424_DCS_SET_MDDI            0xAE
-
-/*
- * Sony seems to use vendor ID 0x81
- */
-#define DISPLAY_SONY_ACX424AKP_ID1     0x811b
-#define DISPLAY_SONY_ACX424AKP_ID2     0x811a
-/*
- * The third ID looks like a bug, vendor IDs begin at 0x80
- * and panel 00 ... seems like default values.
- */
-#define DISPLAY_SONY_ACX424AKP_ID3     0x8000
-
-struct acx424akp {
-       struct drm_panel panel;
-       struct device *dev;
-       struct regulator *supply;
-       struct gpio_desc *reset_gpio;
-       bool video_mode;
-};
-
-static const struct drm_display_mode sony_acx424akp_vid_mode = {
-       .clock = 27234,
-       .hdisplay = 480,
-       .hsync_start = 480 + 15,
-       .hsync_end = 480 + 15 + 0,
-       .htotal = 480 + 15 + 0 + 15,
-       .vdisplay = 864,
-       .vsync_start = 864 + 14,
-       .vsync_end = 864 + 14 + 1,
-       .vtotal = 864 + 14 + 1 + 11,
-       .width_mm = 48,
-       .height_mm = 84,
-       .flags = DRM_MODE_FLAG_PVSYNC,
-};
-
-/*
- * The timings are not very helpful as the display is used in
- * command mode using the maximum HS frequency.
- */
-static const struct drm_display_mode sony_acx424akp_cmd_mode = {
-       .clock = 35478,
-       .hdisplay = 480,
-       .hsync_start = 480 + 154,
-       .hsync_end = 480 + 154 + 16,
-       .htotal = 480 + 154 + 16 + 32,
-       .vdisplay = 864,
-       .vsync_start = 864 + 1,
-       .vsync_end = 864 + 1 + 1,
-       .vtotal = 864 + 1 + 1 + 1,
-       /*
-        * Some desired refresh rate, experiments at the maximum "pixel"
-        * clock speed (HS clock 420 MHz) yields around 117Hz.
-        */
-       .width_mm = 48,
-       .height_mm = 84,
-};
-
-static inline struct acx424akp *panel_to_acx424akp(struct drm_panel *panel)
-{
-       return container_of(panel, struct acx424akp, panel);
-}
-
-#define FOSC                   20 /* 20Mhz */
-#define SCALE_FACTOR_NS_DIV_MHZ        1000
-
-static int acx424akp_set_brightness(struct backlight_device *bl)
-{
-       struct acx424akp *acx = bl_get_data(bl);
-       struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
-       int period_ns = 1023;
-       int duty_ns = bl->props.brightness;
-       u8 pwm_ratio;
-       u8 pwm_div;
-       u8 par;
-       int ret;
-
-       if (backlight_is_blank(bl)) {
-               /* Disable backlight */
-               par = 0x00;
-               ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
-                                        &par, 1);
-               if (ret) {
-                       dev_err(acx->dev, "failed to disable display backlight (%d)\n", ret);
-                       return ret;
-               }
-               return 0;
-       }
-
-       /* Calculate the PWM duty cycle in n/256's */
-       pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1);
-       pwm_div = max(1,
-                     ((FOSC * period_ns) / 256) /
-                     SCALE_FACTOR_NS_DIV_MHZ);
-
-       /* Set up PWM dutycycle ONE byte (differs from the standard) */
-       dev_dbg(acx->dev, "calculated duty cycle %02x\n", pwm_ratio);
-       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
-                                &pwm_ratio, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to set display PWM ratio (%d)\n", ret);
-               return ret;
-       }
-
-       /*
-        * Sequence to write PWMDIV:
-        *      address         data
-        *      0xF3            0xAA   CMD2 Unlock
-        *      0x00            0x01   Enter CMD2 page 0
-        *      0X7D            0x01   No reload MTP of CMD2 P1
-        *      0x22            PWMDIV
-        *      0x7F            0xAA   CMD2 page 1 lock
-        */
-       par = 0xaa;
-       ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to unlock CMD 2 (%d)\n", ret);
-               return ret;
-       }
-       par = 0x01;
-       ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to enter page 1 (%d)\n", ret);
-               return ret;
-       }
-       par = 0x01;
-       ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to disable MTP reload (%d)\n", ret);
-               return ret;
-       }
-       ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to set PWM divisor (%d)\n", ret);
-               return ret;
-       }
-       par = 0xaa;
-       ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to lock CMD 2 (%d)\n", ret);
-               return ret;
-       }
-
-       /* Enable backlight */
-       par = 0x24;
-       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
-                                &par, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to enable display backlight (%d)\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct backlight_ops acx424akp_bl_ops = {
-       .update_status = acx424akp_set_brightness,
-};
-
-static const struct backlight_properties acx424akp_bl_props = {
-       .type = BACKLIGHT_RAW,
-       .brightness = 512,
-       .max_brightness = 1023,
-};
-
-static int acx424akp_read_id(struct acx424akp *acx)
-{
-       struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
-       u8 vendor, version, panel;
-       u16 val;
-       int ret;
-
-       ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID1, &vendor, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "could not vendor ID byte\n");
-               return ret;
-       }
-       ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID2, &version, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "could not read device version byte\n");
-               return ret;
-       }
-       ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID3, &panel, 1);
-       if (ret < 0) {
-               dev_err(acx->dev, "could not read panel ID byte\n");
-               return ret;
-       }
-
-       if (vendor == 0x00) {
-               dev_err(acx->dev, "device vendor ID is zero\n");
-               return -ENODEV;
-       }
-
-       val = (vendor << 8) | panel;
-       switch (val) {
-       case DISPLAY_SONY_ACX424AKP_ID1:
-       case DISPLAY_SONY_ACX424AKP_ID2:
-       case DISPLAY_SONY_ACX424AKP_ID3:
-               dev_info(acx->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n",
-                        vendor, version, panel);
-               break;
-       default:
-               dev_info(acx->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n",
-                        vendor, version, panel);
-               break;
-       }
-
-       return 0;
-}
-
-static int acx424akp_power_on(struct acx424akp *acx)
-{
-       int ret;
-
-       ret = regulator_enable(acx->supply);
-       if (ret) {
-               dev_err(acx->dev, "failed to enable supply (%d)\n", ret);
-               return ret;
-       }
-
-       /* Assert RESET */
-       gpiod_set_value_cansleep(acx->reset_gpio, 1);
-       udelay(20);
-       /* De-assert RESET */
-       gpiod_set_value_cansleep(acx->reset_gpio, 0);
-       usleep_range(11000, 20000);
-
-       return 0;
-}
-
-static void acx424akp_power_off(struct acx424akp *acx)
-{
-       /* Assert RESET */
-       gpiod_set_value_cansleep(acx->reset_gpio, 1);
-       usleep_range(11000, 20000);
-
-       regulator_disable(acx->supply);
-}
-
-static int acx424akp_prepare(struct drm_panel *panel)
-{
-       struct acx424akp *acx = panel_to_acx424akp(panel);
-       struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
-       const u8 mddi = 3;
-       int ret;
-
-       ret = acx424akp_power_on(acx);
-       if (ret)
-               return ret;
-
-       ret = acx424akp_read_id(acx);
-       if (ret) {
-               dev_err(acx->dev, "failed to read panel ID (%d)\n", ret);
-               goto err_power_off;
-       }
-
-       /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
-       ret = mipi_dsi_dcs_set_tear_on(dsi,
-                                      MIPI_DSI_DCS_TEAR_MODE_VBLANK);
-       if (ret) {
-               dev_err(acx->dev, "failed to enable vblank TE (%d)\n", ret);
-               goto err_power_off;
-       }
-
-       /*
-        * Set MDDI
-        *
-        * This presumably deactivates the Qualcomm MDDI interface and
-        * selects DSI, similar code is found in other drivers such as the
-        * Sharp LS043T1LE01 which makes us suspect that this panel may be
-        * using a Novatek NT35565 or similar display driver chip that shares
-        * this command. Due to the lack of documentation we cannot know for
-        * sure.
-        */
-       ret = mipi_dsi_dcs_write(dsi, ACX424_DCS_SET_MDDI,
-                                &mddi, sizeof(mddi));
-       if (ret < 0) {
-               dev_err(acx->dev, "failed to set MDDI (%d)\n", ret);
-               goto err_power_off;
-       }
-
-       /* Exit sleep mode */
-       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
-       if (ret) {
-               dev_err(acx->dev, "failed to exit sleep mode (%d)\n", ret);
-               goto err_power_off;
-       }
-       msleep(140);
-
-       ret = mipi_dsi_dcs_set_display_on(dsi);
-       if (ret) {
-               dev_err(acx->dev, "failed to turn display on (%d)\n", ret);
-               goto err_power_off;
-       }
-       if (acx->video_mode) {
-               /* In video mode turn peripheral on */
-               ret = mipi_dsi_turn_on_peripheral(dsi);
-               if (ret) {
-                       dev_err(acx->dev, "failed to turn on peripheral\n");
-                       goto err_power_off;
-               }
-       }
-
-       return 0;
-
-err_power_off:
-       acx424akp_power_off(acx);
-       return ret;
-}
-
-static int acx424akp_unprepare(struct drm_panel *panel)
-{
-       struct acx424akp *acx = panel_to_acx424akp(panel);
-       struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
-       int ret;
-
-       ret = mipi_dsi_dcs_set_display_off(dsi);
-       if (ret) {
-               dev_err(acx->dev, "failed to turn display off (%d)\n", ret);
-               return ret;
-       }
-
-       /* Enter sleep mode */
-       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
-       if (ret) {
-               dev_err(acx->dev, "failed to enter sleep mode (%d)\n", ret);
-               return ret;
-       }
-       msleep(85);
-
-       acx424akp_power_off(acx);
-
-       return 0;
-}
-
-
-static int acx424akp_get_modes(struct drm_panel *panel,
-                              struct drm_connector *connector)
-{
-       struct acx424akp *acx = panel_to_acx424akp(panel);
-       struct drm_display_mode *mode;
-
-       if (acx->video_mode)
-               mode = drm_mode_duplicate(connector->dev,
-                                         &sony_acx424akp_vid_mode);
-       else
-               mode = drm_mode_duplicate(connector->dev,
-                                         &sony_acx424akp_cmd_mode);
-       if (!mode) {
-               dev_err(panel->dev, "bad mode or failed to add mode\n");
-               return -EINVAL;
-       }
-       drm_mode_set_name(mode);
-       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-
-       connector->display_info.width_mm = mode->width_mm;
-       connector->display_info.height_mm = mode->height_mm;
-
-       drm_mode_probed_add(connector, mode);
-
-       return 1; /* Number of modes */
-}
-
-static const struct drm_panel_funcs acx424akp_drm_funcs = {
-       .unprepare = acx424akp_unprepare,
-       .prepare = acx424akp_prepare,
-       .get_modes = acx424akp_get_modes,
-};
-
-static int acx424akp_probe(struct mipi_dsi_device *dsi)
-{
-       struct device *dev = &dsi->dev;
-       struct acx424akp *acx;
-       int ret;
-
-       acx = devm_kzalloc(dev, sizeof(struct acx424akp), GFP_KERNEL);
-       if (!acx)
-               return -ENOMEM;
-       acx->video_mode = of_property_read_bool(dev->of_node,
-                                               "enforce-video-mode");
-
-       mipi_dsi_set_drvdata(dsi, acx);
-       acx->dev = dev;
-
-       dsi->lanes = 2;
-       dsi->format = MIPI_DSI_FMT_RGB888;
-       /*
-        * FIXME: these come from the ST-Ericsson vendor driver for the
-        * HREF520 and seems to reflect limitations in the PLLs on that
-        * platform, if you have the datasheet, please cross-check the
-        * actual max rates.
-        */
-       dsi->lp_rate = 19200000;
-       dsi->hs_rate = 420160000;
-
-       if (acx->video_mode)
-               /* Burst mode using event for sync */
-               dsi->mode_flags =
-                       MIPI_DSI_MODE_VIDEO |
-                       MIPI_DSI_MODE_VIDEO_BURST;
-       else
-               dsi->mode_flags =
-                       MIPI_DSI_CLOCK_NON_CONTINUOUS;
-
-       acx->supply = devm_regulator_get(dev, "vddi");
-       if (IS_ERR(acx->supply))
-               return PTR_ERR(acx->supply);
-
-       /* This asserts RESET by default */
-       acx->reset_gpio = devm_gpiod_get_optional(dev, "reset",
-                                                 GPIOD_OUT_HIGH);
-       if (IS_ERR(acx->reset_gpio))
-               return dev_err_probe(dev, PTR_ERR(acx->reset_gpio),
-                                    "failed to request GPIO\n");
-
-       drm_panel_init(&acx->panel, dev, &acx424akp_drm_funcs,
-                      DRM_MODE_CONNECTOR_DSI);
-
-       acx->panel.backlight = devm_backlight_device_register(dev, "acx424akp", dev, acx,
-                                       &acx424akp_bl_ops, &acx424akp_bl_props);
-       if (IS_ERR(acx->panel.backlight))
-               return dev_err_probe(dev, PTR_ERR(acx->panel.backlight),
-                                    "failed to register backlight device\n");
-
-       drm_panel_add(&acx->panel);
-
-       ret = mipi_dsi_attach(dsi);
-       if (ret < 0) {
-               drm_panel_remove(&acx->panel);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int acx424akp_remove(struct mipi_dsi_device *dsi)
-{
-       struct acx424akp *acx = mipi_dsi_get_drvdata(dsi);
-
-       mipi_dsi_detach(dsi);
-       drm_panel_remove(&acx->panel);
-
-       return 0;
-}
-
-static const struct of_device_id acx424akp_of_match[] = {
-       { .compatible = "sony,acx424akp" },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, acx424akp_of_match);
-
-static struct mipi_dsi_driver acx424akp_driver = {
-       .probe = acx424akp_probe,
-       .remove = acx424akp_remove,
-       .driver = {
-               .name = "panel-sony-acx424akp",
-               .of_match_table = acx424akp_of_match,
-       },
-};
-module_mipi_dsi_driver(acx424akp_driver);
-
-MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
-MODULE_DESCRIPTION("MIPI-DSI Sony acx424akp Panel Driver");
-MODULE_LICENSE("GPL v2");