drm: add drm driver lcd support
authorEvoke Zhang <evoke.zhang@amlogic.com>
Tue, 16 Jan 2018 10:42:47 +0000 (18:42 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 15 Mar 2018 03:11:56 +0000 (19:11 -0800)
PD#160546: add drm driver lcd support

Change-Id: I3b5fa4c10b4ac542ccdfa8691e692e849f071145
Signed-off-by: Evoke Zhang <evoke.zhang@amlogic.com>
12 files changed:
MAINTAINERS
arch/arm64/boot/dts/amlogic/gxl_p212_1g_buildroot.dts
arch/arm64/boot/dts/amlogic/gxl_p212_2g_buildroot.dts
arch/arm64/boot/dts/amlogic/mesongxl_p212-panel.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
drivers/amlogic/drm/Kconfig
drivers/amlogic/drm/Makefile
drivers/amlogic/drm/am_meson_lcd.c [new file with mode: 0644]
drivers/amlogic/drm/am_meson_lcd.h [new file with mode: 0644]
drivers/amlogic/drm/meson_drv.c
drivers/amlogic/media/vout/lcd/lcd_vout.c
include/linux/amlogic/media/vout/lcd/lcd_notify.h

index 4fe6866..6301330 100644 (file)
@@ -14274,6 +14274,11 @@ AMLOGIC ADD LCD_EXTERN P070ACB DRIVER
 M:     Weiming Liu <weiming.liu@amlogic.com>
 F:     drivers/amlogic/media/vout/lcd/lcd_extern/mipi_P070ACB.c
 
+AMLOGIC DRM LCD DRIVER
+M:      evoke.zhang <evoke.zhang@amlogic.com>
+F:      drivers/amlogic/drm/am_meson_lcd.c
+F:      drivers/amlogic/drm/am_meson_lcd.h
+
 AMLOGIC ADD SOUND EXTERNAL LOOPBACK FEATURE
 M:     Peipeng Zhao <peipeng.zhao@amlogic.com>
 F:     Documentation/devicetree/bindings/amlogic/axg-sound-loopback.txt
index b167eac..08ee87e 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "mesongxl.dtsi"
 #include "meson_drm.dtsi"
+#include "mesongxl_p212-panel.dtsi"
 / {
        model = "Amlogic";
        amlogic-dt-id = "gxl_p212_1g";
index 5df91ce..a044e01 100644 (file)
@@ -20,6 +20,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include "mesongxl.dtsi"
 #include "meson_drm.dtsi"
+#include "mesongxl_p212-panel.dtsi"
 / {
        model = "Amlogic";
        amlogic-dt-id = "gxl_p212_2g";
diff --git a/arch/arm64/boot/dts/amlogic/mesongxl_p212-panel.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl_p212-panel.dtsi
new file mode 100644 (file)
index 0000000..0d125f4
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * arch/arm64/boot/dts/amlogic/mesongxl_p212-panel.dtsi
+ *
+ * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+/ {
+       lcd{
+               compatible = "amlogic, lcd-gxl";
+               dev_name = "lcd";
+               mode = "tablet";
+               status = "okay";
+               key_valid = <0>;
+               /* clocks = <&clkc CLKID_VCLK2_ENCL
+                *      &clkc CLKID_VCLK2_VENCL>;
+                * clock-names = "vencl_top_gate",
+                * "vencl_int_gate";
+                */
+               reg = <0x0 0xc8834400 0x0 0x100>;
+               pinctrl_version = <1>; /* for uboot */
+
+               /* power type:
+                *    (0=cpu_gpio, 1=pmu_gpio, 2=signal,3=extern, 0xff=ending)
+                * power index:
+                *    (point gpios_index, or extern_index,0xff=invalid)
+                * power value:(0=output low, 1=output high, 2=input)
+                * power delay:(unit in ms)
+                */
+               /*lcd_cpu-gpios = <&gpio GPIOX_3 1>;*/
+               /*lcd_cpu_gpio_names = "GPIOX_3";*/
+
+               lcd_0{
+                       model_name = "LCD720P";
+                       interface = "ttl";
+                       basic_setting = <1280 720 /*h_active, v_active*/
+                               1650 750 /*h_period, v_period*/
+                               8      /*lcd_bits */
+                               16 9>; /*screen_widht, screen_height*/
+                       lcd_timing = <40 220 1 /*hs_width, hs_bp, hs_pol*/
+                               5 20 1>; /*vs_width, vs_bp, vs_pol*/
+                       clk_attr = <0 /*fr_adj_type(0=clk, 1=htotal, 2=vtotal)*/
+                               0 /*clk_ss_level*/
+                               1 /*clk_auto_generate*/
+                               74250000>; /*pixel_clk(unit in Hz)*/
+                       ttl_attr = <0 /*clk_pol*/
+                               1 /*de_valid*/
+                               1 /*hvsync_valid*/
+                               0 /*rb_swap*/
+                               0>; /*bit_swap*/
+
+                       /* power step: type, index, value, delay(ms) */
+                       power_on_step = <2 0 0 0
+                               0xff 0 0 0>; /*ending*/
+                       power_off_step = <2 0 0 50
+                               0xff 0 0 0>; /*ending*/
+                       backlight_index = <0xff>;
+               };
+       };
+};/* end of panel */
+
index 5092715..a2adb89 100644 (file)
@@ -17,7 +17,7 @@
 
 / {
        lcd{
-               compatible = "amlogic, lcd";
+               compatible = "amlogic, lcd-gxm";
                dev_name = "lcd";
                mode = "tablet";
                status = "okay";
@@ -27,6 +27,7 @@
                 * clock-names = "vencl_top_gate",
                 * "vencl_int_gate";
                 */
+               reg = <0x0 0xc8834400 0x0 0x100>;
                pinctrl_version = <1>; /* for uboot */
                pinctrl-names = "ttl_6bit_hvsync_de_on","ttl_6bit_hvsync_on",
                        "ttl_6bit_de_on","ttl_8bit_hvsync_de_on",
index 2a2a7d8..f265305 100644 (file)
@@ -17,6 +17,19 @@ config DRM_MESON_BYPASS_MODE
                We add bypass mode to implment drm based on amlogic existing driver,
                it should be easier and more robust.
 
+config DRM_MESON_PANEL
+       bool "support drm panel function for meson drm display."
+       default y
+       depends on DRM_MESON
+       depends on AMLOGIC_LCD
+       select DRM_PANEL
+       select DRM_MIPI_DSI
+       help
+               add drm panel support.
+               use internal amlogic media vout lcd driver.
+               We should confirm AMLOGIC_LCD is configured if
+               DRM_MESON_PANEL is selected.
+
 config DRM_MESON_USE_ION
        bool "gem use ion to alloc/free graphic buffer."
        default y
index 658f284..15f5dea 100644 (file)
@@ -16,4 +16,8 @@ ifeq ($(CONFIG_DRM_MESON_EMULATE_FBDEV),y)
         meson-y += am_meson_fbdev.o
 endif
 
+ifeq ($(CONFIG_DRM_MESON_PANEL),y)
+        meson-y += am_meson_lcd.o
+endif
+
 obj-$(CONFIG_DRM_MESON) += meson.o
diff --git a/drivers/amlogic/drm/am_meson_lcd.c b/drivers/amlogic/drm/am_meson_lcd.c
new file mode 100644 (file)
index 0000000..88b35c0
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * drivers/amlogic/drm/am_meson_lcd.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <drm/drm_modeset_helper.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_mipi_dsi.h>
+#include <video/display_timing.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+
+#include "meson_drv.h"
+#include "am_meson_lcd.h"
+
+struct am_drm_lcd_s {
+       struct drm_panel panel;
+       struct drm_connector connector;
+       struct drm_encoder encoder;
+       struct mipi_dsi_host dsi_host;
+       struct drm_device *drm;
+       struct aml_lcd_drv_s *lcd_drv;
+       struct drm_display_mode *mode;
+       struct display_timing *timing;
+};
+
+static struct am_drm_lcd_s *am_drm_lcd;
+
+static struct drm_display_mode am_lcd_mode = {
+       .name = "panel",
+       .status = 0,
+       .clock = 74250,
+       .hdisplay = 1280,
+       .hsync_start = 1390,
+       .hsync_end = 1430,
+       .htotal = 1650,
+       .hskew = 0,
+       .vdisplay = 720,
+       .vsync_start = 725,
+       .vsync_end = 730,
+       .vtotal = 750,
+       .vscan = 0,
+       .vrefresh = 60,
+};
+
+static struct display_timing am_lcd_timing = {
+       .pixelclock = { 55000000, 65000000, 75000000 },
+       .hactive = { 1024, 1024, 1024 },
+       .hfront_porch = { 40, 40, 40 },
+       .hback_porch = { 220, 220, 220 },
+       .hsync_len = { 20, 60, 100 },
+       .vactive = { 768, 768, 768 },
+       .vfront_porch = { 7, 7, 7 },
+       .vback_porch = { 21, 21, 21 },
+       .vsync_len = { 10, 10, 10 },
+       .flags = DISPLAY_FLAGS_DE_HIGH,
+};
+
+/* ***************************************************************** */
+/*     drm driver function                                           */
+/* ***************************************************************** */
+#if 0
+static inline struct am_drm_lcd_s *host_to_lcd(struct mipi_dsi_host *host)
+{
+       return container_of(host, struct am_drm_lcd_s, dsi_host);
+}
+#endif
+
+static inline struct am_drm_lcd_s *con_to_lcd(struct drm_connector *con)
+{
+       return container_of(con, struct am_drm_lcd_s, connector);
+}
+
+static inline struct am_drm_lcd_s *encoder_to_lcd(struct drm_encoder *encoder)
+{
+       return container_of(encoder, struct am_drm_lcd_s, encoder);
+}
+
+static inline struct am_drm_lcd_s *panel_to_lcd(struct drm_panel *panel)
+{
+       return container_of(panel, struct am_drm_lcd_s, panel);
+}
+
+static int am_lcd_connector_get_modes(struct drm_connector *connector)
+{
+       struct drm_display_mode *mode;
+       struct am_drm_lcd_s *lcd;
+       int count = 0;
+
+       lcd = con_to_lcd(connector);
+
+       pr_info("***************************************************\n");
+       pr_info("am_drm_lcd: %s: lcd mode [%s] display size: %d x %d\n",
+               __func__, lcd->mode->name,
+               lcd->mode->hdisplay, lcd->mode->vdisplay);
+
+       mode = drm_mode_duplicate(connector->dev, lcd->mode);
+       pr_info("am_drm_lcd: %s: drm mode [%s] display size: %d x %d\n",
+               __func__, mode->name, mode->hdisplay, mode->vdisplay);
+       pr_info("am_drm_lcd: %s: lcd config size: %d x %d\n",
+               __func__, lcd->lcd_drv->lcd_config->lcd_basic.h_active,
+               lcd->lcd_drv->lcd_config->lcd_basic.v_active);
+
+       drm_mode_probed_add(connector, mode);
+       count = 1;
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       pr_info("***************************************************\n");
+
+       return count;
+}
+
+enum drm_mode_status am_lcd_connector_mode_valid(
+               struct drm_connector *connector,
+               struct drm_display_mode *mode)
+{
+       struct am_drm_lcd_s *lcd;
+
+       lcd = con_to_lcd(connector);
+       if (!lcd)
+               return MODE_ERROR;
+       if (!lcd->lcd_drv)
+               return MODE_ERROR;
+
+       pr_info("am_drm_lcd: %s: mode [%s] display size: %d x %d\n",
+               __func__, mode->name, mode->hdisplay, mode->vdisplay);
+       pr_info("am_drm_lcd: %s: lcd config size: %d x %d\n",
+               __func__, lcd->lcd_drv->lcd_config->lcd_basic.h_active,
+               lcd->lcd_drv->lcd_config->lcd_basic.v_active);
+
+       if (mode->hdisplay != lcd->lcd_drv->lcd_config->lcd_basic.h_active)
+               return MODE_BAD_WIDTH;
+       if (mode->vdisplay != lcd->lcd_drv->lcd_config->lcd_basic.v_active)
+               return MODE_BAD_WIDTH;
+
+       pr_info("am_drm_lcd: %s %d: check mode OK\n", __func__, __LINE__);
+
+       return MODE_OK;
+}
+
+
+static const struct drm_connector_helper_funcs am_lcd_connector_helper_funcs = {
+       .get_modes = am_lcd_connector_get_modes,
+       .mode_valid = am_lcd_connector_mode_valid,
+       //.best_encoder
+       //.atomic_best_encoder
+};
+
+static enum drm_connector_status am_lcd_connector_detect(
+               struct drm_connector *connector, bool force)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       return connector_status_connected;
+}
+
+#if 0
+static const struct drm_connector_funcs am_lcd_connector_funcs = {
+       .dpms                   = drm_atomic_helper_connector_dpms,
+       .detect                 = am_lcd_connector_detect,
+       .fill_modes             = drm_helper_probe_single_connector_modes,
+       .destroy                = drm_connector_cleanup,
+       .reset                  = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
+};
+#else
+
+static int am_lcd_connector_dpms(struct drm_connector *connector, int mode)
+{
+       int ret = 0;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       ret = drm_atomic_helper_connector_dpms(connector, mode);
+       return ret;
+}
+
+static int am_lcd_connector_fill_modes(struct drm_connector *connector,
+               uint32_t maxX, uint32_t maxY)
+{
+       int count = 0;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       count = drm_helper_probe_single_connector_modes(connector, maxX, maxY);
+       pr_info("am_drm_lcd: %s %d: count=%d\n", __func__, __LINE__, count);
+       return count;
+}
+
+static void am_lcd_connector_destroy(struct drm_connector *connector)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       drm_connector_cleanup(connector);
+}
+
+static void am_lcd_connector_reset(struct drm_connector *connector)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       drm_atomic_helper_connector_reset(connector);
+}
+
+static struct drm_connector_state *am_lcd_connector_duplicate_state(
+               struct drm_connector *connector)
+{
+       struct drm_connector_state *state;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       state = drm_atomic_helper_connector_duplicate_state(connector);
+       return state;
+}
+
+static void am_lcd_connector_destroy_state(struct drm_connector *connector,
+                                         struct drm_connector_state *state)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       drm_atomic_helper_connector_destroy_state(connector, state);
+}
+
+static const struct drm_connector_funcs am_lcd_connector_funcs = {
+       .dpms                   = am_lcd_connector_dpms,
+       .detect                 = am_lcd_connector_detect,
+       .fill_modes             = am_lcd_connector_fill_modes,
+       .destroy                = am_lcd_connector_destroy,
+       .reset                  = am_lcd_connector_reset,
+       .atomic_duplicate_state = am_lcd_connector_duplicate_state,
+       .atomic_destroy_state   = am_lcd_connector_destroy_state,
+};
+#endif
+
+static void am_lcd_encoder_mode_set(struct drm_encoder *encoder,
+                                  struct drm_display_mode *mode,
+                                  struct drm_display_mode *adjusted_mode)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+}
+
+static void am_lcd_encoder_enable(struct drm_encoder *encoder)
+{
+       enum vmode_e vmode = get_current_vmode();
+       struct am_drm_lcd_s *lcd = encoder_to_lcd(encoder);
+
+       if (!lcd)
+               return;
+       if (!lcd->lcd_drv)
+               return;
+
+       if (vmode == VMODE_LCD)
+               DRM_INFO("am_lcd_encoder_enable\n");
+       else
+               DRM_INFO("am_lcd_encoder_enable fail! vmode:%d\n", vmode);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &vmode);
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_PREPARE, NULL);
+       aml_lcd_notifier_call_chain(LCD_EVENT_ENABLE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &vmode);
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+}
+
+static void am_lcd_encoder_disable(struct drm_encoder *encoder)
+{
+       struct am_drm_lcd_s *lcd = encoder_to_lcd(encoder);
+
+       if (!lcd)
+               return;
+       if (!lcd->lcd_drv)
+               return;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_DISABLE, NULL);
+       aml_lcd_notifier_call_chain(LCD_EVENT_UNPREPARE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+}
+
+static void am_lcd_encoder_commit(struct drm_encoder *encoder)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+}
+
+static int am_lcd_encoder_atomic_check(struct drm_encoder *encoder,
+                               struct drm_crtc_state *crtc_state,
+                               struct drm_connector_state *conn_state)
+{
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+       return 0;
+}
+
+static const struct drm_encoder_helper_funcs am_lcd_encoder_helper_funcs = {
+       .commit = am_lcd_encoder_commit,
+       .mode_set = am_lcd_encoder_mode_set,
+       .enable = am_lcd_encoder_enable,
+       .disable = am_lcd_encoder_disable,
+       .atomic_check = am_lcd_encoder_atomic_check,
+};
+
+static const struct drm_encoder_funcs am_lcd_encoder_funcs = {
+       .destroy        = drm_encoder_cleanup,
+};
+
+static int am_lcd_disable(struct drm_panel *panel)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+
+       if (!lcd)
+               return -ENODEV;
+       if (!lcd->lcd_drv)
+               return -ENODEV;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_DISABLE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 0;
+}
+
+static int am_lcd_unprepare(struct drm_panel *panel)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+
+       if (!lcd)
+               return -ENODEV;
+       if (!lcd->lcd_drv)
+               return -ENODEV;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_UNPREPARE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 0;
+}
+
+static int am_lcd_prepare(struct drm_panel *panel)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+
+       if (!lcd)
+               return -ENODEV;
+       if (!lcd->lcd_drv)
+               return -ENODEV;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_PREPARE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 0;
+}
+
+static int am_lcd_enable(struct drm_panel *panel)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+
+       if (!lcd)
+               return -ENODEV;
+       if (!lcd->lcd_drv)
+               return -ENODEV;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       mutex_lock(&lcd->lcd_drv->power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_ENABLE, NULL);
+       mutex_unlock(&lcd->lcd_drv->power_mutex);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 0;
+}
+
+static int am_lcd_get_modes(struct drm_panel *panel)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+       struct drm_connector *connector = panel->connector;
+       struct drm_device *drm = panel->drm;
+       struct drm_display_mode *mode;
+       struct lcd_config_s *pconf;
+
+       if (!lcd->mode)
+               return 0;
+
+       mode = drm_mode_duplicate(drm, lcd->mode);
+       if (!mode) {
+               pr_err("error: am_drm_lcd: failed to add mode %ux%u@%u\n",
+                       mode->hdisplay, mode->vdisplay, mode->vrefresh);
+       }
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       mode->type |= DRM_MODE_TYPE_DRIVER;
+       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+       drm_mode_set_name(mode);
+
+       drm_mode_probed_add(connector, mode);
+
+       pconf = lcd->lcd_drv->lcd_config;
+       connector->display_info.bpc = pconf->lcd_basic.lcd_bits * 3;
+       connector->display_info.width_mm = pconf->lcd_basic.screen_width;
+       connector->display_info.height_mm = pconf->lcd_basic.screen_height;
+
+       connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 1;
+}
+
+static int am_lcd_get_timings(struct drm_panel *panel,
+                                   unsigned int num_timings,
+                                   struct display_timing *timings)
+{
+       struct am_drm_lcd_s *lcd = panel_to_lcd(panel);
+
+       if (!lcd)
+               return 0;
+       if (!lcd->timing)
+               return 0;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       num_timings = 1;
+
+       if (timings)
+               memcpy(&timings[0], lcd->timing, sizeof(struct display_timing));
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return 1;
+}
+
+static const struct drm_panel_funcs am_drm_lcd_funcs = {
+       .disable = am_lcd_disable,
+       .unprepare = am_lcd_unprepare,
+       .prepare = am_lcd_prepare,
+       .enable = am_lcd_enable,
+       .get_modes = am_lcd_get_modes,
+       .get_timings = am_lcd_get_timings,
+};
+
+static void am_drm_lcd_display_mode_timing_init(struct am_drm_lcd_s *lcd)
+{
+       struct lcd_config_s *pconf;
+       unsigned short tmp;
+
+       if (!lcd->lcd_drv) {
+               pr_info("invalid lcd driver\n");
+               return;
+       }
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       pconf = lcd->lcd_drv->lcd_config;
+
+       lcd->mode = &am_lcd_mode;
+       lcd->timing = &am_lcd_timing;
+
+       lcd->mode->clock = pconf->lcd_timing.lcd_clk / 1000;
+       lcd->mode->hdisplay = pconf->lcd_basic.h_active;
+       tmp = pconf->lcd_basic.h_period - pconf->lcd_basic.h_active -
+                       pconf->lcd_timing.hsync_bp;
+       lcd->mode->hsync_start = pconf->lcd_basic.h_active + tmp -
+                       pconf->lcd_timing.hsync_width;
+       lcd->mode->hsync_end = pconf->lcd_basic.h_active + tmp;
+       lcd->mode->htotal = pconf->lcd_basic.h_period;
+       lcd->mode->vdisplay = pconf->lcd_basic.v_active;
+       tmp = pconf->lcd_basic.v_period - pconf->lcd_basic.v_active -
+                       pconf->lcd_timing.vsync_bp;
+       lcd->mode->vsync_start = pconf->lcd_basic.v_active + tmp -
+                       pconf->lcd_timing.vsync_width;
+       lcd->mode->vsync_end = pconf->lcd_basic.v_active + tmp;
+       lcd->mode->vtotal = pconf->lcd_basic.v_period;
+       lcd->mode->width_mm = pconf->lcd_basic.screen_width;
+       lcd->mode->height_mm = pconf->lcd_basic.screen_height;
+       lcd->mode->vrefresh = pconf->lcd_timing.sync_duration_num /
+                               pconf->lcd_timing.sync_duration_den;
+
+       lcd->timing->pixelclock.min = pconf->lcd_timing.lcd_clk;
+       lcd->timing->pixelclock.typ = pconf->lcd_timing.lcd_clk;
+       lcd->timing->pixelclock.max = pconf->lcd_timing.lcd_clk;
+       lcd->timing->hactive.min = pconf->lcd_basic.h_active;
+       lcd->timing->hactive.typ = pconf->lcd_basic.h_active;
+       lcd->timing->hactive.max = pconf->lcd_basic.h_active;
+       tmp = pconf->lcd_basic.h_period - pconf->lcd_basic.h_active -
+               pconf->lcd_timing.hsync_bp - pconf->lcd_timing.hsync_width;
+       lcd->timing->hfront_porch.min = tmp;
+       lcd->timing->hfront_porch.typ = tmp;
+       lcd->timing->hfront_porch.max = tmp;
+       lcd->timing->hback_porch.min = pconf->lcd_timing.hsync_bp;
+       lcd->timing->hback_porch.typ = pconf->lcd_timing.hsync_bp;
+       lcd->timing->hback_porch.max = pconf->lcd_timing.hsync_bp;
+       lcd->timing->hsync_len.min = pconf->lcd_timing.hsync_width;
+       lcd->timing->hsync_len.typ = pconf->lcd_timing.hsync_width;
+       lcd->timing->hsync_len.max = pconf->lcd_timing.hsync_width;
+       lcd->timing->vactive.min = pconf->lcd_basic.v_active;
+       lcd->timing->vactive.typ = pconf->lcd_basic.v_active;
+       lcd->timing->vactive.max = pconf->lcd_basic.v_active;
+       tmp = pconf->lcd_basic.v_period - pconf->lcd_basic.v_active -
+               pconf->lcd_timing.vsync_bp - pconf->lcd_timing.vsync_width;
+       lcd->timing->vfront_porch.min = tmp;
+       lcd->timing->vfront_porch.typ = tmp;
+       lcd->timing->vfront_porch.max = tmp;
+       lcd->timing->vback_porch.min = pconf->lcd_timing.vsync_bp;
+       lcd->timing->vback_porch.typ = pconf->lcd_timing.vsync_bp;
+       lcd->timing->vback_porch.max = pconf->lcd_timing.vsync_bp;
+       lcd->timing->vsync_len.min = pconf->lcd_timing.vsync_width;
+       lcd->timing->vsync_len.typ = pconf->lcd_timing.vsync_width;
+       lcd->timing->vsync_len.max = pconf->lcd_timing.vsync_width;
+
+       pr_info("am_drm_lcd: %s: lcd config:\n"
+               "lcd_clk             %d\n"
+               "h_active            %d\n"
+               "v_active            %d\n"
+               "screen_width        %d\n"
+               "screen_height       %d\n"
+               "sync_duration_den   %d\n"
+               "sync_duration_num   %d\n",
+               __func__,
+               lcd->lcd_drv->lcd_config->lcd_timing.lcd_clk,
+               lcd->lcd_drv->lcd_config->lcd_basic.h_active,
+               lcd->lcd_drv->lcd_config->lcd_basic.v_active,
+               lcd->lcd_drv->lcd_config->lcd_basic.screen_width,
+               lcd->lcd_drv->lcd_config->lcd_basic.screen_height,
+               lcd->lcd_drv->lcd_config->lcd_timing.sync_duration_den,
+               lcd->lcd_drv->lcd_config->lcd_timing.sync_duration_num);
+       pr_info("am_drm_lcd: %s: display mode:\n"
+               "clock       %d\n"
+               "hdisplay    %d\n"
+               "vdisplay    %d\n"
+               "width_mm    %d\n"
+               "height_mm   %d\n"
+               "vrefresh    %d\n",
+               __func__,
+               lcd->mode->clock,
+               lcd->mode->hdisplay,
+               lcd->mode->vdisplay,
+               lcd->mode->width_mm,
+               lcd->mode->height_mm,
+               lcd->mode->vrefresh);
+       pr_info("am_drm_lcd: %s: timing:\n"
+               "pixelclock   %d\n"
+               "hactive      %d\n"
+               "vactive      %d\n",
+               __func__,
+               lcd->timing->pixelclock.typ,
+               lcd->timing->hactive.typ,
+               lcd->timing->vactive.typ);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+}
+
+int am_drm_lcd_register(struct drm_device *drm)
+{
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       int encoder_type, connector_type;
+       int ret = 0;
+
+       am_drm_lcd = kzalloc(sizeof(*am_drm_lcd), GFP_KERNEL);
+       if (!am_drm_lcd)
+               return -ENOMEM;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       am_drm_lcd->lcd_drv = aml_lcd_get_driver();
+       if (!am_drm_lcd->lcd_drv) {
+               pr_err("invalid lcd driver, exit\n");
+               return -ENODEV;
+       }
+
+       am_drm_lcd_display_mode_timing_init(am_drm_lcd);
+
+       drm_panel_init(&am_drm_lcd->panel);
+       am_drm_lcd->panel.dev = NULL;
+       am_drm_lcd->panel.funcs = &am_drm_lcd_funcs;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       ret = drm_panel_add(&am_drm_lcd->panel);
+       if (ret < 0)
+               return ret;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       am_drm_lcd->drm = drm;
+
+       encoder = &am_drm_lcd->encoder;
+       connector = &am_drm_lcd->connector;
+       encoder_type = DRM_MODE_ENCODER_LVDS;
+       connector_type = DRM_MODE_CONNECTOR_LVDS;
+
+       /* Encoder */
+       drm_encoder_helper_add(encoder, &am_lcd_encoder_helper_funcs);
+       ret = drm_encoder_init(drm, encoder, &am_lcd_encoder_funcs,
+                              encoder_type, "am_lcd_encoder");
+       if (ret) {
+               pr_err("error: am_drm_lcd: Failed to init lcd encoder\n");
+               return ret;
+       }
+       pr_info("am_drm_lcd: %s %d: encoder possible_crtcs=%d\n",
+               __func__, __LINE__, encoder->possible_crtcs);
+
+       /* Connector */
+       drm_connector_helper_add(connector, &am_lcd_connector_helper_funcs);
+       ret = drm_connector_init(drm, connector, &am_lcd_connector_funcs,
+                               connector_type);
+       if (ret) {
+               pr_err("error: am_drm_lcd: Failed to init lcd connector\n");
+               return ret;
+       }
+
+       /* force possible_crtcs */
+       encoder->possible_crtcs = BIT(0);
+
+       drm_mode_connector_attach_encoder(connector, encoder);
+
+       pr_info("am_drm_lcd: register ok\n");
+
+       return ret;
+}
+
+int am_drm_lcd_unregister(struct drm_device *drm)
+{
+       int ret = 0;
+
+       if (!am_drm_lcd)
+               return -ENODEV;
+
+       if (!am_drm_lcd->lcd_drv)
+               return -ENODEV;
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       drm_panel_detach(&am_drm_lcd->panel);
+       drm_panel_remove(&am_drm_lcd->panel);
+
+       pr_info("am_drm_lcd: %s %d\n", __func__, __LINE__);
+
+       return ret;
+}
diff --git a/drivers/amlogic/drm/am_meson_lcd.h b/drivers/amlogic/drm/am_meson_lcd.h
new file mode 100644 (file)
index 0000000..24bf2d1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * drivers/amlogic/drm/am_meson_lcd.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AM_DRM_LCD_H
+#define __AM_DRM_LCD_H
+
+#include "meson_drv.h"
+
+extern int am_drm_lcd_register(struct drm_device *drm);
+extern int am_drm_lcd_unregister(struct drm_device *drm);
+
+#endif
+
index 1538274..9f47bea 100644 (file)
@@ -51,6 +51,7 @@
 #include "meson_canvas.h"
 #include "meson_registers.h"
 #endif
+#include "am_meson_lcd.h"
 #ifdef CONFIG_DRM_MESON_USE_ION
 #include "am_meson_gem.h"
 #include "am_meson_fb.h"
@@ -586,6 +587,7 @@ static int meson_drv_probe(struct platform_device *pdev)
        meson_vpp_init(priv);
        meson_viu_init(priv);
 #endif
+       am_drm_lcd_register(drm);
 
        ret = meson_plane_create(priv);
        if (ret)
@@ -629,6 +631,8 @@ static int meson_drv_remove(struct platform_device *pdev)
 {
        struct drm_device *drm = dev_get_drvdata(&pdev->dev);
 
+       am_drm_lcd_unregister(drm);
+
        if (meson_drv_use_osd())
                return meson_drv_remove_prune(pdev);
 
index 81d0406..91b2a45 100644 (file)
@@ -419,7 +419,7 @@ static int lcd_power_if_on_notifier(struct notifier_block *nb,
        } else {
                LCDERR("%s: can't power on when controller is off\n",
                                __func__);
-                       return NOTIFY_DONE;
+               return NOTIFY_DONE;
        }
 
        return NOTIFY_OK;
index f373899..e76822a 100644 (file)
@@ -74,6 +74,7 @@
 /* lcd bist pattern test occurred */
 #define LCD_EVENT_TEST_PATTERN      (1 << 14)
 
+
 extern int aml_lcd_notifier_register(struct notifier_block *nb);
 extern int aml_lcd_notifier_unregister(struct notifier_block *nb);
 extern int aml_lcd_notifier_call_chain(unsigned long event, void *v);