drm: add dptx connector driver support [1/1]

PD#SWPL-220560

Problem:
no dptx connector driver

Solution:
add dptx connector driver support

Verify:
t7c

Test:
DRM-TX-38

Change-Id: I3315c549217fc964cfdd23e09b8243a7b43a5d9e
Signed-off-by: Ao Xu <ao.xu@amlogic.com>
This commit is contained in:
Ao Xu
2025-07-16 16:27:23 +08:00
committed by gerrit autosubmit
parent cc465adbbd
commit 6a9faa4e9f
26 changed files with 4210 additions and 21 deletions
+2 -1
View File
@@ -477,7 +477,8 @@ CONFIG_AMLOGIC_USBCAM=m
# aml_drm.ko
CONFIG_AMLOGIC_DRM=m
CONFIG_AMLOGIC_DRM_VPU=y
CONFIG_AMLOGIC_DRM_HDMI=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_DP=y
CONFIG_AMLOGIC_DRM_USE_ION=y
# aml_uvm.ko
@@ -184,6 +184,7 @@ CONFIG_AMLOGIC_SECMON=y
CONFIG_AMLOGIC_DOLBY_FW=y
CONFIG_AMLOGIC_DRM=y
CONFIG_AMLOGIC_DRM_VPU=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_USE_ION=y
CONFIG_AMLOGIC_MEDIA_MODULE=y
CONFIG_AMLOGIC_MEDIA_ENABLE=y
@@ -15,6 +15,7 @@ CONFIG_AMLOGIC_MEDIA_TVIN_BT656=n
CONFIG_AMLOGIC_MEDIA_CAMERA=n
CONFIG_AMLOGIC_DRM_HDMI=n
CONFIG_AMLOGIC_DRM_CUT_HDMI=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_CUT_CVBS=y
# amlogic-snd-codec-t9015.ko
+2 -1
View File
@@ -526,7 +526,8 @@ CONFIG_AMLOGIC_USBCAM=m
# aml_drm.ko
CONFIG_AMLOGIC_DRM=m
CONFIG_AMLOGIC_DRM_VPU=y
CONFIG_AMLOGIC_DRM_HDMI=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_DP=y
CONFIG_AMLOGIC_DRM_USE_ION=y
# aml_uvm.ko
@@ -251,7 +251,6 @@ CONFIG_SERIAL_DEV_BUS=y
CONFIG_HW_RANDOM=y
CONFIG_I2C_CHARDEV=y
CONFIG_SPI=y
CONFIG_SPI_MEM=y
CONFIG_SPI_SPIDEV=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_SINGLE=y
@@ -421,6 +420,7 @@ CONFIG_AMLOGIC_SECMON=y
CONFIG_AMLOGIC_DOLBY_FW=y
CONFIG_AMLOGIC_DRM=m
CONFIG_AMLOGIC_DRM_VPU=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_USE_ION=y
CONFIG_AMLOGIC_UVM=m
CONFIG_AMLOGIC_UVM_ALLOCATOR=y
@@ -513,7 +513,6 @@ CONFIG_AMLOGIC_MEDIA_FRC=y
CONFIG_AMLOGIC_DVB_CORE=m
CONFIG_AMLOGIC_POWER=m
CONFIG_AMLOGIC_POWER_PMIC=y
CONFIG_AMLOGIC_BATTERY_PMIC6B=y
CONFIG_AMLOGIC_CPU_INFO=m
CONFIG_AMLOGIC_SHOW_CPU_CHIPID=y
CONFIG_AMLOGIC_PM=m
@@ -532,14 +531,12 @@ CONFIG_AMLOGIC_PINCTRL_MESON_C1=m
CONFIG_AMLOGIC_PINCTRL_MESON_A4=m
CONFIG_AMLOGIC_PINCTRL_MESON_A5=m
CONFIG_AMLOGIC_GPIO_PMIC=y
CONFIG_AMLOGIC_GPIO_PMIC6B=y
CONFIG_AMLOGIC_INPUT=m
CONFIG_AMLOGIC_GPIO_KEY=y
CONFIG_AMLOGIC_ADC_KEYPADS=y
CONFIG_AMLOGIC_CKS05_TOUCH_KEYPADS=y
CONFIG_AMLOGIC_MESON_IR=y
CONFIG_AMLOGIC_INPUT_PMIC=y
CONFIG_AMLOGIC_PWRKEY_PMIC6B=y
CONFIG_AMLOGIC_I2C_MESON=m
CONFIG_AMLOGIC_SPI=m
CONFIG_AMLOGIC_SPI_MESON_SPICC=y
@@ -601,7 +598,6 @@ CONFIG_AMLOGIC_DOS_RESET_MESON=m
CONFIG_AMLOGIC_RTC=m
CONFIG_AMLOGIC_RTC_DRV_MESON_VRTC=y
CONFIG_AMLOGIC_MESON_RTC=y
CONFIG_AMLOGIC_RTC_PMIC6B=y
CONFIG_AMLOGIC_IRBLASTER=m
CONFIG_AMLOGIC_AMLOGIC_THERMAL=m
CONFIG_AMLOGIC_COOLDEV=y
@@ -648,8 +644,6 @@ CONFIG_AMLOGIC_DWMAC_DWC_QOS_ETH=m
CONFIG_AMLOGIC_DWMAC_MESON=m
CONFIG_AMLOGIC_MTD_SPI_NAND=m
CONFIG_AMLOGIC_MTD_NAND=m
CONFIG_AMLOGIC_MTD_COMMON=m
CONFIG_AMLOGIC_MTD_RESV=m
CONFIG_AMLOGIC_DVB_CONFIG=m
CONFIG_AMLOGIC_DVB_EXTERN=y
CONFIG_AMLOGIC_DVB_DSM=y
@@ -662,12 +656,10 @@ CONFIG_AMLOGIC_LEDS_AW9523B=y
CONFIG_AMLOGIC_LEDS_PCA9557=y
CONFIG_AMLOGIC_USBCAM=m
CONFIG_AMLOGIC_MFD=m
CONFIG_AMLOGIC_MFD_PMIC6B=y
CONFIG_AMLOGIC_REGULATOR=m
CONFIG_AMLOGIC_REGULATOR_PWM=y
CONFIG_AMLOGIC_REGULATOR_PWM_MODIFY=y
CONFIG_AMLOGIC_PMIC_REGULATOR=y
CONFIG_AMLOGIC_REGULATOR_PMIC6B=y
CONFIG_AMLOGIC_SND_SOC_CODECS=y
CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC=m
CONFIG_AMLOGIC_SND_CODEC_AMLT9015=m
@@ -15,6 +15,7 @@ CONFIG_AMLOGIC_MEDIA_TVIN_BT656=n
CONFIG_AMLOGIC_MEDIA_CAMERA=n
CONFIG_AMLOGIC_DRM_HDMI=n
CONFIG_AMLOGIC_DRM_CUT_HDMI=y
CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN=y
CONFIG_AMLOGIC_DRM_CUT_CVBS=y
# amlogic-snd-codec-t9015.ko
+13
View File
@@ -28,6 +28,7 @@ ddk_module(
"meson_vpu_util.c",
"meson_drm_bind.c",
"meson_dummy.c",
"meson_venc.c",
"meson_drm_rdma.c",
"vpu-hw/meson_vpu_video_wrapper.c",
"vpu-hw/meson_vpu_osd_mif.c",
@@ -55,6 +56,18 @@ ddk_module(
"meson_hdmitx_diag.c",
],
},
"CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN": {
False: [
"meson_hdmi_modern.c",
"meson_tx_helper.c",
],
},
"CONFIG_AMLOGIC_DRM_DP": {
True: [
"meson_dptx.c",
"meson_tx_helper.c",
],
},
"CONFIG_AMLOGIC_DRM_CUT_CVBS": {
False: [
"meson_cvbs.c",
+18
View File
@@ -32,6 +32,24 @@ config AMLOGIC_DRM_CUT_HDMI
We should confirm AMLOGIC_HDMITX is configured if
AMLOGIC_DRM_HDMI is selected.
config AMLOGIC_DRM_CUT_HDMI_MODERN
bool "support drm hdmi function for meson drm display."
depends on !(AMLOGIC_HDMITX_MODERN || AMLOGIC_HDMITX21_MODERN)
help
add drm hdmi support.
use internal amlogic media vout hdmi driver.
We should confirm AMLOGIC_HDMITX_MODERN is configured if
AMLOGIC_DRM_HDMI is selected.
config AMLOGIC_DRM_DP
bool "support drm dp function for meson drm display."
depends on AMLOGIC_DPTX
help
add drm dp support.
use internal amlogic media vout dp driver.
We should confirm AMLOGIC_DPTX is configured if
AMLOGIC_DRM_DP is selected.
config AMLOGIC_DRM_CUT_CVBS
bool "support drm cvbs function for meson drm display."
depends on !AMLOGIC_CVBS_OUTPUT
+11
View File
@@ -26,6 +26,16 @@ ifeq ($(CONFIG_AMLOGIC_DRM_CUT_HDMI),)
$(MESON_DRM_MODULE_NAME)-y += meson_hdmitx_diag.o
endif
ifeq ($(CONFIG_AMLOGIC_DRM_DP),y)
$(MESON_DRM_MODULE_NAME)-y += meson_dptx.o
$(MESON_DRM_MODULE_NAME)-y += meson_tx_helper.o
endif
ifeq ($(CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN),)
$(MESON_DRM_MODULE_NAME)-y += meson_hdmi_modern.o
$(MESON_DRM_MODULE_NAME)-y += meson_tx_helper.o
endif
ifeq ($(CONFIG_AMLOGIC_DRM_CUT_CVBS),)
$(MESON_DRM_MODULE_NAME)-y += meson_cvbs.o
endif
@@ -41,6 +51,7 @@ $(MESON_DRM_MODULE_NAME)-y += meson_drv.o meson_plane.o \
meson_drm_bind.o\
meson_dummy.o \
meson_drm_rdma.o \
meson_venc.o \
vpu-hw/meson_vpu_video_wrapper.o \
vpu-hw/meson_vpu_osd_mif.o \
vpu-hw/meson_osd_gfcd.o \
+617
View File
@@ -0,0 +1,617 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/of_platform.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_modeset_lock.h>
#include <drm/amlogic/meson_connector_dev.h>
#include <linux/amlogic/media/vout/meson_tx_connector/dptx_common/dptx_common.h>
#include <vout/vout_serve/vout_func.h>
#include "meson_crtc.h"
#include "meson_dptx.h"
#include "meson_venc.h"
struct meson_dptx_connector_state {
struct drm_connector_state base;
struct tx_color_attr color_attr_para;
struct meson_tx_state base_state;
};
#define to_dptx_connector_state(x) container_of(x, struct meson_dptx_connector_state, base)
#define encoder_to_meson_dptx(x) container_of(x, struct meson_dp_tx, encoder)
#define connector_to_meson_dptx(x) \
container_of(connector_to_meson_connector(x), struct meson_dp_tx, base)
static struct dptx_common *connector_to_dptx_common(struct drm_connector *connector)
{
struct meson_dp_tx *meson_dptx = NULL;
meson_dptx = connector_to_meson_dptx(connector);
return conn_to_dptx_common(meson_dptx->dptx_dev);
}
static struct meson_tx_dev *conn_dev_to_meson_tx_dev(struct device *dev)
{
struct dptx_common *dptx_comm = NULL;
struct drm_connector *connector = dev_get_drvdata(dev);
dptx_comm = connector_to_dptx_common(connector);
return &dptx_comm->base;
}
static int meson_dptx_mode_probed_add(int count, struct tx_timing *timings,
struct drm_connector *connector)
{
int i;
struct drm_display_mode *mode;
struct tx_timing *timing = timings;
for (i = 0; i < count; i++, timing++) {
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("drm mode create failed.\n");
continue;
}
meson_connector_fill_mode_timing(mode, timing, false);
drm_mode_probed_add(connector, mode);
DRM_DEBUG("add mode [%s]\n", mode->name);
}
return 0;
}
static int meson_dptx_get_modes(struct drm_connector *connector)
{
struct edid *edid;
struct meson_dp_tx *meson_dptx = connector_to_meson_dptx(connector);
struct dptx_common *tx_comm = connector_to_dptx_common(connector);
struct meson_dptx_connector_state *dptx_state =
to_dptx_connector_state(connector->state);
struct tx_timing *timing_list = NULL;
u64 sequence_id, new_sequence_id;
int count = 0;
if (!meson_dptx || !meson_dptx->dptx_dev || !tx_comm)
return count;
edid = (struct edid *)meson_tx_get_raw_edid(&tx_comm->base);
drm_connector_update_edid_property(connector, edid);
sequence_id = dptx_state->base_state.sequence_id;
new_sequence_id = meson_tx_get_hpd_hw_sequence_id(&tx_comm->base);
/*
* After set mode, hwc will update the connector.
* In order to prevent the edid from being parsed every time,
* the sequence_id judgment is added, and the edid is only parsed
* when the hot_plug time occurs.
*/
if (sequence_id != new_sequence_id) {
dptx_state->base_state.sequence_id = new_sequence_id;
meson_tx_clear_rx_cap(&tx_comm->base.rxcap);
meson_tx_edid_parse(&tx_comm->base.rxcap, tx_comm->base.edid_buf,
tx_comm->base.edid_parse_mask);
DRM_INFO("%s[%d]\n", __func__, __LINE__);
}
count = dptx_get_mode_list(tx_comm, &timing_list);
if (count > 0) {
meson_dptx_mode_probed_add(count, timing_list, connector);
vfree(timing_list);
timing_list = NULL;
}
DRM_INFO("%s get %d modes\n", __func__, count);
return 0;
}
static enum drm_mode_status meson_dptx_check_mode(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static int meson_dptx_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
struct meson_dptx_connector_state *new_state, *curr_state;
if (!state) {
DRM_ERROR("state is NULL.\n");
return -EINVAL;
}
curr_state = to_dptx_connector_state
(drm_atomic_get_old_connector_state(state, connector));
new_state = to_dptx_connector_state
(drm_atomic_get_new_connector_state(state, connector));
DRM_DEBUG("%s %px %px\n", __func__, curr_state, new_state);
return 0;
}
static struct drm_encoder *meson_dptx_best_encoder(struct drm_connector *connector)
{
struct meson_dp_tx *meson_dptx = connector_to_meson_dptx(connector);
return &meson_dptx->encoder;
}
static const struct drm_connector_helper_funcs meson_dptx_connector_helper_funcs = {
.get_modes = meson_dptx_get_modes,
.mode_valid = meson_dptx_check_mode,
.atomic_check = meson_dptx_atomic_check,
.best_encoder = meson_dptx_best_encoder,
};
static enum drm_connector_status meson_dptx_connector_detect
(struct drm_connector *connector, bool force)
{
struct dptx_common *tx_comm = connector_to_dptx_common(connector);
int hpd_state = meson_tx_get_hpd_state(&tx_comm->base);
DRM_DEBUG("dptx_connector_detect [%d]\n", hpd_state);
return hpd_state == 1 ?
connector_status_connected : connector_status_disconnected;
}
static int meson_dptx_connector_atomic_set_property
(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property, uint64_t val)
{
struct meson_dp_tx *meson_dptx = connector_to_meson_dptx(connector);
struct meson_dptx_connector_state *dptx_state =
to_dptx_connector_state(state);
struct tx_color_attr *attr = &dptx_state->color_attr_para;
if (property == meson_dptx->color_depth_prop)
attr->bitdepth = val;
else if (property == meson_dptx->color_space_prop)
attr->colorformat = val;
else
return -EINVAL;
return 0;
}
static int meson_dptx_connector_atomic_get_property
(struct drm_connector *connector,
const struct drm_connector_state *state,
struct drm_property *property, uint64_t *val)
{
struct meson_dp_tx *meson_dptx = connector_to_meson_dptx(connector);
struct meson_dptx_connector_state *dptx_state =
to_dptx_connector_state(state);
struct tx_color_attr *attr = &dptx_state->color_attr_para;
if (property == meson_dptx->color_depth_prop)
*val = attr->bitdepth;
else if (property == meson_dptx->color_space_prop)
*val = attr->colorformat;
else
return -EINVAL;
return 0;
}
static void meson_dptx_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static void meson_dptx_reset(struct drm_connector *connector)
{
struct dptx_common *dptx_comm;
struct meson_tx_state *tx_state;
struct meson_dptx_connector_state *dptx_state;
dptx_comm = connector_to_dptx_common(connector);
dptx_state = kzalloc(sizeof(*dptx_state), GFP_KERNEL);
if (!dptx_state)
return;
if (connector->state)
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, &dptx_state->base);
tx_state = &dptx_state->base_state;
tx_state->sequence_id = ~0ULL;
}
struct drm_connector_state *meson_dptx_atomic_duplicate_state
(struct drm_connector *connector)
{
struct meson_tx_state *tx_state, *curr_tx_state;
struct meson_dptx_connector_state *new_state;
struct meson_dptx_connector_state *curr_state =
to_dptx_connector_state(connector->state);
new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
if (!new_state)
return NULL;
__drm_atomic_helper_connector_duplicate_state(connector,
&new_state->base);
tx_state = &new_state->base_state;
curr_tx_state = &curr_state->base_state;
tx_state->sequence_id = curr_tx_state->sequence_id;
return &new_state->base;
}
static void meson_dptx_atomic_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
struct meson_dptx_connector_state *dptx_state;
dptx_state = to_dptx_connector_state(state);
__drm_atomic_helper_connector_destroy_state(&dptx_state->base);
kfree(dptx_state);
}
static void meson_dptx_atomic_print_state(struct drm_printer *p,
const struct drm_connector_state *state)
{
}
static int meson_dptx_late_register(struct drm_connector *connector)
{
struct dptx_common *dptx_comm = connector_to_dptx_common(connector);
meson_tx_sysfs_create(&dptx_comm->base);
return 0;
}
static void meson_dptx_early_unregister(struct drm_connector *connector)
{
struct dptx_common *dptx_comm = connector_to_dptx_common(connector);
meson_tx_sysfs_remove(&dptx_comm->base);
}
static const struct drm_connector_funcs meson_dptx_connector_funcs = {
.detect = meson_dptx_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_set_property = meson_dptx_connector_atomic_set_property,
.atomic_get_property = meson_dptx_connector_atomic_get_property,
.destroy = meson_dptx_connector_destroy,
.reset = meson_dptx_reset,
.atomic_duplicate_state = meson_dptx_atomic_duplicate_state,
.atomic_destroy_state = meson_dptx_atomic_destroy_state,
.atomic_print_state = meson_dptx_atomic_print_state,
.late_register = meson_dptx_late_register,
.early_unregister = meson_dptx_early_unregister,
};
static void meson_dptx_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc_state->crtc);
struct meson_dp_tx *meson_dptx = encoder_to_meson_dptx(encoder);
struct meson_connector_dev *tx_dev = meson_dptx->dptx_dev;
update_curr_vout_server(amcrtc->base.index + 1, tx_dev->vout_serv);
}
static void meson_dptx_encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_connector_state *conn_state, *old_conn_state;
struct meson_dptx_connector_state *dptx_conn_state, *old_dptx_conn_state;
struct drm_crtc_state *crtc_state;
struct am_meson_crtc_state *meson_crtc_state;
struct am_meson_crtc *amcrtc;
struct drm_display_mode *mode;
struct meson_dp_tx *meson_dptx = encoder_to_meson_dptx(encoder);
struct meson_connector_dev *conn_dev = meson_dptx->dptx_dev;
struct meson_tx_dev *tx_dev = to_meson_tx_dev(conn_dev);
struct drm_connector *conn = &meson_dptx->base.connector;
conn_state = drm_atomic_get_new_connector_state(state, conn);
dptx_conn_state = to_dptx_connector_state(conn_state);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
old_dptx_conn_state = to_dptx_connector_state(old_conn_state);
amcrtc = to_am_meson_crtc(conn_state->crtc);
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
mode = &crtc_state->adjusted_mode;
meson_crtc_state = to_am_meson_crtc_state(crtc_state);
meson_vout_notify_mode_change(amcrtc->vout_index,
meson_crtc_state->vmode, EVENT_MODE_SET_START);
meson_tx_do_mode_setting(tx_dev, &dptx_conn_state->base_state,
&old_dptx_conn_state->base_state);
meson_venc_mode_set(meson_dptx->venc, meson_dptx->enc_idx, VENC_ENCP,
&dptx_conn_state->base_state.para);
meson_vout_notify_mode_change(amcrtc->vout_index,
meson_crtc_state->vmode, EVENT_MODE_SET_FINISH);
meson_vout_update_mode_name(amcrtc->vout_index, mode->name, "dptx");
}
static void meson_dptx_encoder_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct am_meson_crtc *amcrtc;
struct drm_connector_state *old_conn_state;
struct meson_dp_tx *meson_dptx = encoder_to_meson_dptx(encoder);
struct drm_connector *conn = &meson_dptx->base.connector;
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
amcrtc = to_am_meson_crtc(old_conn_state->crtc);
meson_venc_mode_disable(meson_dptx->venc, meson_dptx->enc_idx);
}
static enum drm_mode_status meson_dptx_encoder_mode_valid(struct drm_encoder *crtc,
const struct drm_display_mode *mode)
{
return MODE_OK;
}
static int meson_dptx_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
int ret = 0;
int frac_mode = 0;
struct tx_timing *timing;
struct dptx_common *dptx_comm = connector_to_dptx_common(conn_state->connector);
struct am_meson_crtc_state *meson_crtc_state =
to_am_meson_crtc_state(crtc_state);
struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
struct meson_dptx_connector_state *dptx_state =
to_dptx_connector_state(conn_state);
struct meson_tx_state *tx_state = &dptx_state->base_state;
struct tx_color_attr *attr = &dptx_state->color_attr_para;
dptx_state = to_dptx_connector_state(conn_state);
timing = kzalloc(sizeof(*timing), GFP_KERNEL);
if (!timing)
return -ENOMEM;
meson_drm_mode_build_tx_timing(adj_mode, timing);
meson_tx_format_para_init(&tx_state->para, timing,
frac_mode, attr->colorformat,
bitdepth_to_colordepth(attr->bitdepth), 0);
ret = meson_tx_validate_mode(&dptx_comm->base, tx_state);
if (!ret)
meson_crtc_state->preset_vmode = VMODE_DisplayPort;
else
DRM_ERROR("%s validate fail %d\n", __func__, ret);
kfree(timing);
return ret;
}
static const struct drm_encoder_helper_funcs meson_dptx_encoder_helper_funcs = {
.atomic_mode_set = meson_dptx_encoder_atomic_mode_set,
.atomic_enable = meson_dptx_encoder_atomic_enable,
.atomic_disable = meson_dptx_encoder_atomic_disable,
.atomic_check = meson_dptx_encoder_atomic_check,
.mode_valid = meson_dptx_encoder_mode_valid,
};
static const struct drm_encoder_funcs meson_dptx_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static void meson_dptx_update(struct drm_connector_state *new_state,
struct drm_connector_state *old_state)
{
}
static void meson_dptx_hpd_cb(void *data)
{
struct meson_connector *meson_con = (struct meson_connector *)data;
struct drm_connector *connector = &meson_con->connector;
DRM_INFO("drm hdmitx hpd notify\n");
drm_kms_helper_hotplug_event(connector->dev);
}
/* Optional colorspace properties. */
static const struct drm_prop_enum_list color_space_enum_list[] = {
{ HDMI_COLORSPACE_RGB, "RGB" },
{ HDMI_COLORSPACE_YUV422, "422" },
{ HDMI_COLORSPACE_YUV444, "444" },
{ HDMI_COLORSPACE_YUV420, "420" },
{ HDMI_COLORSPACE_RESERVED6, "HDMI_COLORSPACE_RESERVED6" }
};
static void meson_dptx_init_colorspace_property(struct drm_device *drm_dev,
struct meson_dp_tx *meson_dptx,
struct meson_tx_state *state)
{
struct drm_property *prop;
struct dptx_common *dptx_comm;
int colorformat = state->para.cs;
dptx_comm = conn_to_dptx_common(meson_dptx->dptx_dev);
prop = drm_property_create_enum(drm_dev, 0, "color_space",
color_space_enum_list,
ARRAY_SIZE(color_space_enum_list));
if (prop) {
meson_dptx->color_space_prop = prop;
drm_object_attach_property(&meson_dptx->base.connector.base, prop,
colorformat);
} else {
DRM_ERROR("Failed to color_space property\n");
}
}
static void meson_dptx_init_colordepth_property(struct drm_device *drm_dev,
struct meson_dp_tx *meson_dptx,
struct meson_tx_state *state)
{
struct drm_property *prop;
struct dptx_common *dptx_comm;
int bitdepth = state->para.cd;
dptx_comm = conn_to_dptx_common(meson_dptx->dptx_dev);
prop = drm_property_create_range(drm_dev, 0,
"color_depth", 0, 16);
if (prop) {
meson_dptx->color_depth_prop = prop;
drm_object_attach_property(&meson_dptx->base.connector.base, prop,
colordepth_to_bitdepth(bitdepth));
} else {
DRM_ERROR("Failed to color_depth property\n");
}
}
static int meson_drm_probe_venc(struct meson_dp_tx *meson_dptx, struct device *dev)
{
struct platform_device *venc_pdev;
struct device_node *venc_node;
venc_node = of_parse_phandle(dev->of_node, "tx_venc", 0);
if (!venc_node) {
dev_err(dev, "cannot find venc device\n");
return -ENXIO;
}
venc_pdev = of_find_device_by_node(venc_node);
if (venc_pdev) {
meson_dptx->venc = platform_get_drvdata(venc_pdev);
meson_dptx->venc_pdev = venc_pdev;
}
of_node_put(venc_node);
if (!venc_pdev) {
dev_err(dev, "%s: venc driver is not ready\n", __func__);
return -EPROBE_DEFER;
}
if (!meson_dptx->venc) {
put_device(&venc_pdev->dev);
dev_err(dev, "%s: venc driver is not ready\n", __func__);
return -EPROBE_DEFER;
}
return 0;
}
static void meson_dptx_parse_venc_idx(struct device *dev, u32 *enc_idx)
{
int ret;
ret = of_property_read_u32(dev->of_node, "enc_idx", enc_idx);
if (ret)
DRM_ERROR("enc_idx invalid\n");
}
int meson_dptx_dev_bind(struct drm_device *drm,
int type, struct meson_connector_dev *intf)
{
int ret;
struct meson_drm *priv = drm->dev_private;
struct meson_dp_tx *meson_dptx;
struct meson_tx_dev *tx_dev;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct meson_connector *mesonconn;
struct dptx_common *tx_comm;
int encoder_type = DRM_MODE_ENCODER_TMDS;
struct connector_hpd_cb hpd_cb;
struct meson_tx_state *tx_state;
meson_dptx = devm_kzalloc(drm->dev, sizeof(*meson_dptx), GFP_KERNEL);
if (!meson_dptx)
return -ENOMEM;
meson_dptx->dptx_dev = intf;
tx_dev = to_meson_tx_dev(intf);
tx_comm = to_dptx_common(tx_dev);
mesonconn = &meson_dptx->base;
mesonconn->drm_priv = priv;
mesonconn->update = meson_dptx_update;
mesonconn->connector_type = type;
encoder = &meson_dptx->encoder;
connector = &meson_dptx->base.connector;
intf->conn = connector;
intf->connector_type = DRM_MODE_CONNECTOR_DisplayPort;
meson_drm_probe_venc(meson_dptx, priv->dev);
meson_dptx_parse_venc_idx(tx_dev->pdev, &meson_dptx->enc_idx);
/* Connector */
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->ycbcr_420_allowed = true;
drm_connector_helper_add(connector,
&meson_dptx_connector_helper_funcs);
ret = drm_connector_init(drm, connector, &meson_dptx_connector_funcs,
DRM_MODE_CONNECTOR_DisplayPort);
if (ret) {
dev_err(priv->dev, "Failed to init dp tx connector\n");
return ret;
}
/* Encoder */
encoder->possible_crtcs = priv->of_conf.crtc_masks[ENCODER_HDMI];
drm_encoder_helper_add(encoder, &meson_dptx_encoder_helper_funcs);
ret = drm_encoder_init(drm, encoder, &meson_dptx_encoder_funcs,
encoder_type, "dptx_encoder");
if (ret) {
dev_err(priv->dev, "Failed to init hdmi encoder\n");
return ret;
}
drm_connector_attach_encoder(connector, encoder);
/* register hpd callback */
hpd_cb.callback = meson_dptx_hpd_cb;
hpd_cb.data = mesonconn;
meson_tx_register_hpd_cb(tx_dev, &hpd_cb);
/* register connector sysfs device translate to meson_tx_dev */
dptx_register_dev_to_txdev_xlate(tx_dev, conn_dev_to_meson_tx_dev);
/* init dptx property */
tx_state = devm_kzalloc(drm->dev, sizeof(*tx_state), GFP_KERNEL);
if (!tx_state)
return -ENOMEM;
meson_tx_get_init_state(tx_dev, tx_state);
meson_dptx_init_colordepth_property(drm, meson_dptx, tx_state);
meson_dptx_init_colorspace_property(drm, meson_dptx, tx_state);
kfree(tx_state);
return 0;
}
int meson_dptx_dev_unbind(struct drm_device *drm,
int type, struct meson_connector_dev *intf)
{
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#ifndef __MESON_DPTX_H__
#define __MESON_DPTX_H__
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
#include <drm/amlogic/meson_connector_dev.h>
#include "meson_drv.h"
#include "meson_tx_helper.h"
struct meson_dp_tx {
struct meson_connector base;
struct drm_encoder encoder;
struct meson_connector_dev *dptx_dev;
u32 enc_idx;
struct meson_tx_venc *venc;
struct platform_device *venc_pdev;
/*
* Whether the current edid is valid
* 0:edid is invalid
* 1:edid is valid
*/
struct drm_property *edid_valid_prop;
struct drm_property *color_space_prop;
struct drm_property *color_depth_prop;
};
int meson_dptx_dev_bind(struct drm_device *drm,
int type, struct meson_connector_dev *intf);
int meson_dptx_dev_unbind(struct drm_device *drm,
int type, struct meson_connector_dev *intf);
#endif
+18
View File
@@ -4,7 +4,15 @@
*/
#include <drm/amlogic/meson_drm_bind.h>
#ifndef CONFIG_AMLOGIC_DRM_CUT_HDMI
#include "meson_hdmi.h"
#endif
#ifndef CONFIG_AMLOGIC_DRM_CUT_HDMI_MODERN
#include "meson_hdmi_modern.h"
#endif
#include "meson_dptx.h"
#include "meson_cvbs.h"
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
#include "meson_lcd.h"
@@ -52,6 +60,11 @@ int meson_connector_dev_bind(struct drm_device *drm,
return meson_hdmitx_dev_bind(drm, type, intf);
#endif
#ifdef CONFIG_AMLOGIC_DRM_DP
case DRM_MODE_CONNECTOR_DisplayPort:
return meson_dptx_dev_bind(drm, type, intf);
#endif
#ifndef CONFIG_AMLOGIC_DRM_CUT_CVBS
case DRM_MODE_CONNECTOR_TV:
return meson_cvbs_dev_bind(drm, type, intf);
@@ -108,6 +121,11 @@ int meson_connector_dev_unbind(struct drm_device *drm,
return meson_hdmitx_dev_unbind(drm, type, intf);
#endif
#ifdef CONFIG_AMLOGIC_DRM_DP
case DRM_MODE_CONNECTOR_DisplayPort:
return meson_dptx_dev_unbind(drm, type, intf);
#endif
#ifndef CONFIG_AMLOGIC_DRM_CUT_CVBS
case DRM_MODE_CONNECTOR_TV:
return meson_cvbs_dev_unbind(drm, type, intf);
+9
View File
@@ -30,6 +30,14 @@ static int __init meson_drm_main_init(void)
DRM_ERROR("am_meson_vpu_init fail\n");
return ret;
}
DRM_DEBUG("%s() start[%d]\n", __func__, __LINE__);
ret = meson_tx_venc_init();
if (ret) {
DRM_ERROR("meson_tx_venc_init fail\n");
return ret;
}
ret = am_meson_drm_init();
if (ret) {
DRM_ERROR("am_meson_drm_init fail\n");
@@ -43,6 +51,7 @@ static void __exit meson_drm_main_exit(void)
{
DRM_DEBUG("%s() start[%d]\n", __func__, __LINE__);
am_meson_vpu_exit();
meson_tx_venc_exit();
am_meson_drm_exit();
DRM_DEBUG("%s() end[%d]\n", __func__, __LINE__);
}
+2
View File
@@ -17,6 +17,8 @@ static inline int am_meson_vpu_init(void)
static inline void am_meson_vpu_exit(void) {}
#endif
int meson_tx_venc_init(void);
void meson_tx_venc_exit(void);
int am_meson_drm_init(void);
void am_meson_drm_exit(void);
File diff suppressed because it is too large Load Diff
+184
View File
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#ifndef __MESON_HDMI_H__
#define __MESON_HDMI_H__
#include <uapi/amlogic/drm/meson_drm.h>
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
#include <drm/amlogic/meson_connector_dev.h>
#include <linux/amlogic/media/vout/meson_tx_connector/hdmitx_common/hdmitx_common.h>
#include <media/cec-notifier.h>
#include "meson_drv.h"
#include "meson_tx_helper.h"
enum {
HDCP_STATE_START = 0,
HDCP_STATE_SUCCESS,
HDCP_STATE_FAIL,
HDCP_STATE_STOP,
HDCP_STATE_DISCONNECT,
};
enum {
MESON_PREF_DV = 0,
MESON_PREF_HDR,
MESON_PREF_SDR,
};
struct meson_hdr_static_metadata {
u8 lumi_max;
u8 lumi_min;
u8 lumi_avg;
};
enum meson_color_attr_type {
COLOR_YCBCR420_12BIT = 0,
COLOR_YCBCR420_10BIT,
COLOR_YCBCR420_8BIT,
COLOR_YCBCR444_12BIT,
COLOR_YCBCR444_10BIT,
COLOR_YCBCR444_8BIT,
COLOR_YCBCR422_12BIT,
COLOR_RGB_12BIT,
COLOR_RGB_10BIT,
COLOR_RGB_8BIT,
COLOR_MAX_ATTR,
};
struct am_hdmi_tx {
struct meson_connector base;
struct drm_encoder encoder;
/*drm request content type.*/
int hdcp_request_content_type;
int hdcp_request_content_protection;
/*current hdcp running mode, HDCP_NULL means hdcp disabled.*/
int hdcp_mode;
/*hdcp auth result, HDCP_AUTH_UNKNOWN means havenot finished auth.*/
int hdcp_state;
int hdcp_rx_type;
int hdmitx_on;
int min_vfreq;
int max_vfreq;
/* save sequence_id for drm connecter get raw edid */
u64 sequence_id;
/*TODO: android compatible, remove later*/
bool android_path;
bool recovery_mode;
/*
* HWC enable hdcp flow
* 0: IVCX chip don't need: T7/S5/S6/S7/S7D/S1A
* 1: SNPS chip need: SC2/S4/G12/SM1
*/
struct drm_property *hdcp_user_prop;
struct drm_property *hdcp_topo_prop;
struct drm_property *hdcp_ver_prop;
struct drm_property *hdcp_mode_prop;
/* May be changed by hdr_priority */
struct drm_property *hdr_cap_prop;
/* TV's real hdr capability that not changed by hdr_priority */
struct drm_property *hdr_cap_rx_prop;
struct drm_property *dv_cap_prop;
/* TV's real dv capability that not changed by hdr_priority */
struct drm_property *dv_cap_rx_prop;
struct drm_property *dc_cap_prop;
struct drm_property *contenttype_cap_prop;
struct drm_property *allm_cap_prop;
struct drm_property *allm_prop;
/*amlogic property: force hdmitx update
*colorspace/colordepth from sysfs.
*/
struct drm_property *update_attr_prop;
struct drm_property *avmute_prop;
struct drm_property *ready_prop;
struct drm_property *frac_rate_policy_prop;
/*
* if HDMI plugin even once time, then set 1
* if never hdmi plugin, then keep as 0
*/
struct drm_property *hdmi_used_prop;
/*
* the current HDMI RX device type
* 1 none
* 2 repeater
* 4 sink
*/
struct drm_property *sink_type_prop;
/*
* Whether the current edid is valid
* 0:edid is invalid
* 1:edid is valid
*/
struct drm_property *edid_valid_prop;
struct drm_property *color_space_prop;
struct drm_property *color_depth_prop;
struct drm_property *hdmi_hdr_status_prop;
struct drm_property *hdr_priority_prop;
struct drm_property *type_prop;
int hdmi_type;
struct drm_property *static_meta_prop;
struct drm_property *scan_info_prop;
#ifdef CONFIG_CEC_NOTIFIER
struct cec_notifier *cec_notifier;
#endif
struct meson_connector_dev *hdmitx_dev;
struct hdr_info hdr_info;
struct dv_info dv_info;
};
struct am_hdmitx_connector_state {
struct drm_connector_state base;
struct meson_tx_state hcs;
/*drm hdmitx attr from external modules,
*ONLY used for once, and reset when duplicate.
*/
struct tx_color_attr color_attr_para;
/*HDR Priority: dv,hdr,sdr*/
int pref_hdr_policy;
u32 hdr_priority;
enum hdmi_scan_mode scan_info;
bool update : 1;
bool color_force : 1;
bool avmute : 1;
bool ready : 1;
bool frac_rate_policy : 1;
int allm_mode;
struct drm_property_blob *metadata;
};
#define to_am_hdmitx_connector_state(x) container_of(x, struct am_hdmitx_connector_state, base)
#define meson_connector_to_am_hdmi(x) container_of(x, struct am_hdmi_tx, base)
#define connector_to_am_hdmi(x) \
container_of(connector_to_meson_connector(x), struct am_hdmi_tx, base)
#define encoder_to_am_hdmi(x) container_of(x, struct am_hdmi_tx, encoder)
int meson_hdmitx_dev_bind(struct drm_device *drm,
int type, struct meson_connector_dev *intf);
int meson_hdmitx_dev_unbind(struct drm_device *drm,
int type, struct meson_connector_dev *intf);
void convert_attrstr(char *attr_str, struct tx_color_attr *attr_param);
int am_meson_mode_testattr_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int am_meson_get_vrr_range_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
#endif
+166
View File
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <drm/drm_modes.h>
#include <drm/drm_print.h>
#include <linux/amlogic/media/vout/meson_tx_connector/meson_tx_mode.h>
#include "meson_tx_helper.h"
enum hdmi_color_depth bitdepth_to_colordepth(int bitdepth)
{
enum hdmi_color_depth color_depth;
switch (bitdepth) {
case 8:
color_depth = COLORDEPTH_24B;
break;
case 10:
color_depth = COLORDEPTH_30B;
break;
case 12:
color_depth = COLORDEPTH_36B;
break;
case 16:
color_depth = COLORDEPTH_48B;
break;
default:
color_depth = COLORDEPTH_24B;
break;
}
return color_depth;
}
int colordepth_to_bitdepth(enum hdmi_color_depth color_depth)
{
int bitdepth;
switch (color_depth) {
case COLORDEPTH_24B:
bitdepth = 8;
break;
case COLORDEPTH_30B:
bitdepth = 10;
break;
case COLORDEPTH_36B:
bitdepth = 12;
break;
case COLORDEPTH_48B:
bitdepth = 16;
break;
default:
bitdepth = 8;
break;
}
return bitdepth;
}
void meson_connector_fill_mode_timing(struct drm_display_mode *mode,
const struct tx_timing *timing, bool edid_vic)
{
char *strp;
DRM_DEBUG("%s %d %d %d %d %d %d %d %d\n", __func__,
timing->h_active, timing->h_front, timing->h_sync, timing->h_total,
timing->v_active, timing->v_front, timing->v_sync, timing->v_total);
mode->type = DRM_MODE_TYPE_DRIVER;
mode->clock = timing->pixel_freq;
mode->hdisplay = timing->h_active;
mode->hsync_start = timing->h_active + timing->h_front;
mode->hsync_end = timing->h_active + timing->h_front + timing->h_sync;
mode->htotal = timing->h_total;
/* for 480i/576i, horizontal timing is repeated */
if (timing->pixel_repetition_factor) {
mode->flags |= DRM_MODE_FLAG_DBLSCAN;
mode->hdisplay >>= 1;
mode->hsync_start >>= 1;
mode->hsync_end >>= 1;
mode->htotal >>= 1;
mode->clock >>= 1;
}
/*use hskew to distinguish whether it's qms mode or edid mode*/
if (edid_vic)
mode->hskew = 1;
else
mode->hskew = 0;
mode->flags |= timing->h_pol ?
DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
mode->vdisplay = timing->v_active;
mode->vsync_start = timing->v_active + timing->v_front;
mode->vsync_end = timing->v_active + timing->v_front + timing->v_sync;
mode->vtotal = timing->v_total;
mode->vscan = 0;
mode->flags |= timing->v_pol ?
DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
if (!timing->pi_mode)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (timing->sname) {
memcpy(mode->name, timing->sname,
(strlen(timing->sname) < DRM_DISPLAY_MODE_LEN) ?
strlen(timing->sname) : DRM_DISPLAY_MODE_LEN);
} else if (timing->name) {
memcpy(mode->name, timing->name,
(strlen(timing->name) < DRM_DISPLAY_MODE_LEN) ?
strlen(timing->name) : DRM_DISPLAY_MODE_LEN);
} else {
DRM_ERROR(" func %s timing has no name\n", __func__);
sprintf(mode->name, "%ux%up%d", timing->h_active, timing->v_active,
drm_mode_vrefresh(mode));
}
mode->name[DRM_DISPLAY_MODE_LEN - 1] = '\0';
/* remove _4x3 suffix, in case misunderstand */
strp = strstr(mode->name, "_4x3");
if (strp)
*strp = '\0';
}
void meson_drm_mode_build_tx_timing(struct drm_display_mode *mode,
struct tx_timing *timing)
{
if (!mode || !timing)
return;
timing->pixel_freq = mode->clock;
timing->h_active = mode->hdisplay;
timing->h_blank = mode->htotal - mode->hdisplay;
timing->h_sync = mode->hsync_end - mode->hsync_start;
timing->h_front = mode->hsync_start - timing->h_active;
timing->h_total = mode->htotal;
timing->v_active = mode->vdisplay;
timing->v_blank = mode->vtotal - mode->vdisplay;
timing->v_sync = mode->vsync_end - mode->vsync_start;
timing->v_front = mode->vsync_start - timing->v_active;
timing->v_total = mode->vtotal;
timing->pi_mode = mode->flags & DRM_MODE_FLAG_INTERLACE ? 0 : 1;
timing->h_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0;
timing->v_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0;
/* for 480i/576i, horizontal timing is repeated */
if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
timing->pixel_repetition_factor = 1;
timing->h_active <<= 1;
timing->h_total <<= 1;
timing->h_blank <<= 1;
timing->h_sync <<= 1;
timing->h_front <<= 1;
timing->pixel_freq <<= 1;
}
DRM_DEBUG("%s %d %d %d %d %d %d %d %d\n", __func__,
timing->h_active, timing->h_front, timing->h_sync, timing->h_total,
timing->v_active, timing->v_front, timing->v_sync, timing->v_total);
}
+25
View File
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#ifndef __MESON_TX_HELPER_H__
#define __MESON_TX_HELPER_H__
#include <linux/hdmi.h>
#include <linux/amlogic/media/vout/vinfo.h>
struct tx_timing;
struct tx_color_attr {
enum hdmi_colorspace colorformat;
int bitdepth;
};
enum hdmi_color_depth bitdepth_to_colordepth(int bitdepth);
int colordepth_to_bitdepth(enum hdmi_color_depth color_depth);
void meson_connector_fill_mode_timing(struct drm_display_mode *mode,
const struct tx_timing *timing, bool edid_vic);
void meson_drm_mode_build_tx_timing(struct drm_display_mode *mode, struct tx_timing *timing);
#endif
+319
View File
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/amlogic/media/vout/meson_tx_connector/meson_tx_format_para.h>
#include "meson_venc.h"
#include "meson_drm_main.h"
#include "vpu-hw/meson_vpu_reg.h"
#define DEVICE_NAME "meson_tx_venc"
#define MAX_VENC 4
struct meson_tx_venc {
struct platform_device *pdev;
struct regmap *regmap[MAX_VENC];
u32 venc_offset[MAX_VENC];
int num_regmap;
};
static void config_tv_encp_calc(struct regmap *regmap, struct meson_tx_format_para *para)
{
const struct tx_timing *tp = NULL;
struct tx_timing timing = {0};
/* adjust to align upsample and video enable */
u32 hsync_st = 5; // hsync start pixel count
u32 vsync_st = 1; // vsync start line count
// Latency in pixel clock from ENCP_VFIFO2VD request to data ready to HDMI
const u32 vfifo2vd_to_hdmi_latency = 2;
u32 de_h_begin = 0;
u32 de_h_end = 0;
u32 de_v_begin = 0;
u32 de_v_end = 0;
bool y420_mode = 0;
int hpara_div = 1;
if (para->cs == HDMI_COLORSPACE_YUV420)
y420_mode = 1;
tp = &para->timing;
timing = *tp;
timing.h_total /= hpara_div;
timing.h_blank /= hpara_div;
timing.h_front /= hpara_div;
timing.h_sync /= hpara_div;
timing.h_back /= hpara_div;
timing.h_active /= hpara_div;
de_h_end = tp->h_total - (tp->h_front - hsync_st);
de_h_begin = de_h_end - tp->h_active;
de_v_end = tp->v_total - (tp->v_front - vsync_st);
de_v_begin = de_v_end - tp->v_active;
// VENC timing gen is disabled
regmap_write(regmap, ENCP_VIDEO_EN, 0);
// Enable viu vsync interrupt
regmap_write(regmap, VPU_VENC_CTRL, 1);
// set DVI/HDMI transfer timing
// generate hsync
regmap_write(regmap, ENCP_DVI_HSO_BEGIN, hsync_st);
regmap_write(regmap, ENCP_DVI_HSO_END, hsync_st + tp->h_sync);
// generate vsync
regmap_write(regmap, ENCP_DVI_VSO_BLINE_EVN, vsync_st + y420_mode);
regmap_write(regmap, ENCP_DVI_VSO_ELINE_EVN,
vsync_st + tp->v_sync + y420_mode);
regmap_write(regmap, ENCP_DVI_VSO_BEGIN_EVN, hsync_st);
regmap_write(regmap, ENCP_DVI_VSO_END_EVN, hsync_st);
// generate data valid
regmap_write(regmap, ENCP_DE_H_BEGIN, de_h_begin);
regmap_write(regmap, ENCP_DE_H_END, de_h_end);
regmap_write(regmap, ENCP_DE_V_BEGIN_EVEN, de_v_begin);
regmap_write(regmap, ENCP_DE_V_END_EVEN, de_v_end);
// set mode
// Enable Hsync and equalization pulse switch in center; bit[14] cfg_de_v = 1
regmap_write(regmap, ENCP_VIDEO_MODE, 0x0040 | (1 << 14));
regmap_write(regmap, ENCP_VIDEO_MODE_ADV, 0x18); // Sampling rate: 1
// set active region
regmap_write(regmap, ENCP_VIDEO_HAVON_BEGIN, de_h_begin - vfifo2vd_to_hdmi_latency);
regmap_write(regmap, ENCP_VIDEO_HAVON_END, de_h_end - vfifo2vd_to_hdmi_latency - 1);
regmap_write(regmap, ENCP_VIDEO_VAVON_BLINE, de_v_begin);
regmap_write(regmap, ENCP_VIDEO_VAVON_ELINE, de_v_end - 1);
//set hsync
regmap_write(regmap, ENCP_VIDEO_HSO_BEGIN, hsync_st - vfifo2vd_to_hdmi_latency);
regmap_write(regmap, ENCP_VIDEO_HSO_END, hsync_st + tp->h_sync - vfifo2vd_to_hdmi_latency);
//set vsync
regmap_write(regmap, ENCP_VIDEO_VSO_BEGIN, 0);
regmap_write(regmap, ENCP_VIDEO_VSO_END, 0);
regmap_write(regmap, ENCP_VIDEO_VSO_BLINE, vsync_st);
regmap_write(regmap, ENCP_VIDEO_VSO_ELINE, vsync_st + tp->v_sync);
//set vtotal & htotal
regmap_write(regmap, ENCP_VIDEO_MAX_PXCNT, tp->h_total - 1);
regmap_write(regmap, ENCP_VIDEO_MAX_LNCNT, tp->v_total - 1);
// VENC timing gen is disabled
regmap_write(regmap, ENCP_VIDEO_EN, 1);
}
static void config_tv_encl_calc(struct regmap *regmap, struct meson_tx_format_para *para)
{
const struct tx_timing *tp = NULL;
u32 de_h_begin = 0;
u32 de_h_end = 0;
u32 de_v_begin = 0;
u32 de_v_end = 0;
tp = &para->timing;
de_h_begin = tp->h_total - tp->h_front - tp->h_active;
de_h_end = tp->h_total - tp->h_front - 1;
de_v_begin = tp->v_total - tp->v_front - tp->v_active;
de_v_end = tp->v_total - tp->v_front - 1;
// VENC timing gen is disabled
regmap_write(regmap, ENCL_VIDEO_EN, 0);
// bit[15] shadow en
regmap_write(regmap, ENCL_VIDEO_MODE, 0x8000);
// Sampling rate: 1
regmap_write(regmap, ENCL_VIDEO_MODE_ADV, 0x0418);
// bypass filter
regmap_write(regmap, ENCL_VIDEO_FILT_CTRL, 0x1000);
//set vtotal & htotal
regmap_write(regmap, ENCL_VIDEO_MAX_PXCNT, tp->h_total - 1);
regmap_write(regmap, ENCL_VIDEO_MAX_LNCNT, tp->v_total - 1);
regmap_write(regmap, ENCL_VIDEO_HAVON_BEGIN, de_h_begin);
regmap_write(regmap, ENCL_VIDEO_HAVON_END, de_h_end);
regmap_write(regmap, ENCL_VIDEO_VAVON_BLINE, de_v_begin);
regmap_write(regmap, ENCL_VIDEO_VAVON_ELINE, de_v_end);
// set hsync
regmap_write(regmap, ENCL_VIDEO_HSO_BEGIN, 0x0);
regmap_write(regmap, ENCL_VIDEO_HSO_END, tp->h_sync);
// set vsync
regmap_write(regmap, ENCL_VIDEO_VSO_BEGIN, 0x0);
regmap_write(regmap, ENCL_VIDEO_VSO_END, 0x0);
regmap_write(regmap, ENCL_VIDEO_VSO_BLINE, 0x0);
regmap_write(regmap, ENCL_VIDEO_VSO_ELINE, tp->v_sync - 1);
// set inbuf
regmap_write(regmap, ENCL_INBUF_CNTL1, (5 << 13) | (tp->h_sync - 1));
regmap_write(regmap, ENCL_INBUF_CNTL0, 0x200);
regmap_write(regmap, LCD_RGB_BASE_ADDR, 0x0);
regmap_write(regmap, LCD_RGB_COEFF_ADDR, 0x400);
regmap_write(regmap, LCD_DITH_CNTL_ADDR, 0x0);
//regmap_write(regmap, LCD_POL_CNTL_ADDR);
// DE signal
regmap_write(regmap, DE_HS_ADDR, de_h_begin);
regmap_write(regmap, DE_HE_ADDR, de_h_end);
regmap_write(regmap, DE_VS_ADDR, de_v_begin);
regmap_write(regmap, DE_VE_ADDR, de_v_end);
// Hsync signal
regmap_write(regmap, HSYNC_HS_ADDR, 0x0);
regmap_write(regmap, HSYNC_HE_ADDR, tp->h_sync);
regmap_write(regmap, HSYNC_VS_ADDR, 0x0);
regmap_write(regmap, HSYNC_VE_ADDR, tp->v_total - 1);
// Vsync signal
regmap_write(regmap, VSYNC_HS_ADDR, 0x0);
regmap_write(regmap, VSYNC_HE_ADDR, 0x0);
regmap_write(regmap, VSYNC_VS_ADDR, 0x0);
regmap_write(regmap, VSYNC_VE_ADDR, tp->v_sync - 1);
regmap_write(regmap, ENCL_VIDEO_RGBIN_CTRL, 3);
regmap_write(regmap, ENCL_VIDEO_EN, 1);
regmap_write(regmap, VPU_VENC_CTRL, 2);
}
int meson_venc_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type, void *para)
{
struct meson_tx_format_para *fmt_para = (struct meson_tx_format_para *)para;
struct regmap *regmap = venc->regmap[enc_index];
if (enc_type == VENC_ENCP)
config_tv_encp_calc(regmap, fmt_para);
else if (enc_type == VENC_ENCL)
config_tv_encl_calc(regmap, fmt_para);
return 0;
}
int meson_venc_mode_check(struct meson_tx_venc *venc, u32 enc_index, void *para)
{
return 0;
}
int meson_venc_mode_disable(struct meson_tx_venc *venc, u32 enc_index)
{
struct regmap *regmap = venc->regmap[enc_index];
// VENC timing gen is disabled
regmap_write(regmap, ENCP_VIDEO_EN, 0);
return 0;
}
static const struct regmap_config venc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0xffff,
};
static int meson_tx_venc_probe(struct platform_device *pdev)
{
int i, ret, num_venc;
struct resource *res;
void __iomem *base, *venc_base;
struct regmap **regmap;
u32 *venc_offset;
struct meson_tx_venc *tx_venc;
struct device *device = &pdev->dev;
tx_venc = devm_kmalloc(device, sizeof(*tx_venc), GFP_KERNEL);
if (!tx_venc)
return -ENOMEM;
num_venc = of_property_count_u32_elems(device->of_node, "venc_offset");
if (num_venc < 0) {
pr_err("%s Invalid or missing venc_offset\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32_array(device->of_node, "venc_offset",
tx_venc->venc_offset, num_venc);
if (ret) {
pr_err("%s Invalid or missing venc_offset\n", __func__);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
base = devm_ioremap(device, res->start, resource_size(res));
if (!base)
return -ENOMEM;
regmap = tx_venc->regmap;
venc_offset = tx_venc->venc_offset;
tx_venc->num_regmap = num_venc;
for (i = 0; i < tx_venc->num_regmap; i++) {
venc_base = base + (venc_offset[i] << 2);
regmap[i] = devm_regmap_init_mmio(device, venc_base,
&venc_regmap_config);
if (IS_ERR(regmap[i])) {
pr_err("%s regmap init mmio fail\n", __func__);
return -EINVAL;
}
}
dev_set_drvdata(device, tx_venc);
pr_info("%s %px\n", __func__, tx_venc);
return 0;
}
static void meson_tx_venc_remove(struct platform_device *pdev)
{
struct device *device = &pdev->dev;
pr_info("%s %s\n", __func__, dev_name(device));
}
static const struct of_device_id meson_tx_venc_of_match[] = {
{
.compatible = "amlogic, tx-venc-t7c",
},
{
.compatible = "amlogic, tx-venc-a9",
},
{},
};
static struct platform_driver meson_tx_venc_driver = {
.probe = meson_tx_venc_probe,
.remove = meson_tx_venc_remove,
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(meson_tx_venc_of_match),
}
};
int __init meson_tx_venc_init(void)
{
return platform_driver_register(&meson_tx_venc_driver);
}
void __exit meson_tx_venc_exit(void)
{
pr_info("%s\n", __func__);
platform_driver_unregister(&meson_tx_venc_driver);
}
+20
View File
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#ifndef __MESON_VENC_H
#define __MESON_VENC_H
struct meson_tx_venc;
enum venc_type {
VENC_ENCP,
VENC_ENCL,
};
int meson_venc_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type, void *para);
int meson_venc_mode_check(struct meson_tx_venc *venc, u32 enc_index, void *para);
int meson_venc_mode_disable(struct meson_tx_venc *venc, u32 enc_index);
#endif
+10
View File
@@ -753,6 +753,16 @@
#define ENCL_MAX_LINE_SWITCH_POINT 0x1cc8
#define ENCL_DACSEL_0 0x1cc9
#define ENCL_DACSEL_1 0x1cca
#define ENCL_INBUF_CNTL0 0x1cd3
#define ENCL_INBUF_CNTL1 0x1cd4
#define ENCL_INBUF_CNT 0x1cd5
#define LCD_RGB_BASE_ADDR 0x14a5
#define LCD_RGB_COEFF_ADDR 0x14a6
#define LCD_POL_CNTL_ADDR 0x14a7
#define LCD_DITH_CNTL_ADDR 0x14a8
#define RDMA_AHB_START_ADDR_MAN 0x1100
#define RDMA_AHB_END_ADDR_MAN 0x1101
#define RDMA_AHB_START_ADDR_1 0x1102
+31 -9
View File
@@ -26,7 +26,7 @@ static struct vout_module_s vout_module = {
&vout_module.vout_server_list
},
.curr_vout_server = {NULL},
.next_vout_server = NULL,
.next_vout_server = {NULL},
.init_flag = {0},
/* vout_fr_policy:
* 0: disable
@@ -222,16 +222,16 @@ void vout_func_set_state(int index, enum vmode_e mode)
list_for_each_entry(p_server, &p_module->vout_server_list, list) {
data = p_server->data;
if (p_module->next_vout_server && p_server->name &&
p_module->next_vout_server->name &&
(strcmp(p_server->name, p_module->next_vout_server->name) == 0)) {
if (p_module->next_vout_server[index - 1] && p_server->name &&
p_module->next_vout_server[index - 1]->name &&
(strcmp(p_server->name, p_module->next_vout_server[index - 1]->name) == 0)) {
if (vout_debug_print) {
VOUTPR("vout[%d]: %s: valid: server_name=%s, data=%px\n",
index, __func__, p_server->name, data);
}
if (p_server->op.vmode_is_supported(mode, data)) {
p_module->curr_vout_server[index - 1] = p_server;
p_module->next_vout_server = NULL;
p_module->next_vout_server[index - 1] = NULL;
if (p_server->op.set_state)
p_server->op.set_state(index, data);
}
@@ -375,7 +375,7 @@ enum vmode_e vout_func_validate_vmode(int index, char *name, int type, unsigned
mutex_unlock(&vout_mutex);
return VMODE_MAX;
}
p_module->next_vout_server = NULL;
p_module->next_vout_server[index - 1] = NULL;
list_for_each_entry(p_server, &p_module->vout_server_list, list) {
data = p_server->data;
if (vout_debug_print) {
@@ -413,12 +413,12 @@ enum vmode_e vout_func_validate_vmode(int index, char *name, int type, unsigned
if (p_server->op.validate_vmode) {
ret = p_server->op.validate_vmode(name, frac, data);
if (ret != VMODE_MAX) { /* valid vmode find. */
p_module->next_vout_server = p_server;
p_module->next_vout_server[index - 1] = p_server;
if (vout_debug_print) {
VOUTPR("vout[%d]: %s: valid server: name=%s, data=%px\n",
index, __func__,
p_module->next_vout_server->name,
p_module->next_vout_server->data);
p_module->next_vout_server[index - 1]->name,
p_module->next_vout_server[index - 1]->data);
}
break;
}
@@ -430,6 +430,28 @@ enum vmode_e vout_func_validate_vmode(int index, char *name, int type, unsigned
}
EXPORT_SYMBOL(vout_func_validate_vmode);
/*
*interface export to client who want to set current vmode.
*/
void update_curr_vout_server(int index, struct vout_server_s *vout_server)
{
struct vout_module_s *p_module = NULL;
mutex_lock(&vout_mutex);
p_module = &vout_module;
if (!p_module) {
VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__);
mutex_unlock(&vout_mutex);
return;
}
p_module->next_vout_server[index - 1] = vout_server;
mutex_unlock(&vout_mutex);
}
EXPORT_SYMBOL(update_curr_vout_server);
int vout_func_get_disp_cap(int index, char *buf)
{
struct vout_server_s *p_server;
@@ -48,6 +48,7 @@ int vout_func_set_current_vmode(int index, enum vmode_e mode);
int vout_func_check_same_vmodeattr(int index, char *name);
enum vmode_e vout_func_validate_vmode(int index, char *name,
int type, unsigned int frac);
void update_curr_vout_server(int index, struct vout_server_s *vout_server);
int vout_func_get_disp_cap(int index, char *buf);
int vout_func_set_vframe_rate_hint(int index, int duration);
int vout_func_get_vframe_rate_hint(int index);
@@ -593,6 +593,8 @@ int hdmitx_common_validate_mode_locked(struct hdmitx_common *tx_comm,
struct meson_tx_state *new_state,
char *mode, enum hdmi_colorspace cs,
enum hdmi_color_depth cd, bool brr_valid);
int hdmitx_common_check_valid_para_of_vic(struct hdmitx_common *tx_comm,
enum hdmi_vic vic);
int hdmitx_common_disable_mode(struct hdmitx_common *tx_comm,
struct meson_tx_state *new_state);
@@ -8,6 +8,7 @@
#include <linux/hdmi.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/meson_tx_connector/meson_tx_mode.h>
enum frl_rate_enum {
@@ -61,7 +61,7 @@ struct vout_server_s {
struct vout_module_s {
struct list_head vout_server_list;
struct vout_server_s *curr_vout_server[MAX_VOUT];
struct vout_server_s *next_vout_server;
struct vout_server_s *next_vout_server[MAX_VOUT];
unsigned int init_flag[MAX_VOUT];
/* fr_policy: 0=disable, 1=nearby, 2=force */
unsigned int fr_policy[MAX_VOUT];