mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 = ¶->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 = ¶->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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user