drm: add drm driver lcd support

PD#160546: add drm driver lcd support

Change-Id: I3b5fa4c10b4ac542ccdfa8691e692e849f071145
Signed-off-by: Evoke Zhang <evoke.zhang@amlogic.com>
This commit is contained in:
Evoke Zhang
2018-01-16 18:42:47 +08:00
committed by Jianxin Pan
parent e7f28d30cb
commit 7f50914afc
12 changed files with 800 additions and 2 deletions

View 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

View 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";

View 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";

View File

@@ -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 */

View 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",

View 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

View 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

View File

@@ -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;
}

View File

@@ -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

View 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);

View 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;

View 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);