diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 281700b70811..f0723c37fcdf 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -186,6 +186,37 @@ struct rockchip_dsc_sink_cap { u16 target_bits_per_pixel_x16; }; +#define ACM_GAIN_LUT_HY_LENGTH (9*17) +#define ACM_GAIN_LUT_HY_TOTAL_LENGTH (ACM_GAIN_LUT_HY_LENGTH * 3) +#define ACM_GAIN_LUT_HS_LENGTH (13*17) +#define ACM_GAIN_LUT_HS_TOTAL_LENGTH (ACM_GAIN_LUT_HS_LENGTH * 3) +#define ACM_DELTA_LUT_H_LENGTH 65 +#define ACM_DELTA_LUT_H_TOTAL_LENGTH (ACM_DELTA_LUT_H_LENGTH * 3) + +struct post_acm { + s16 delta_lut_h[ACM_DELTA_LUT_H_TOTAL_LENGTH]; + s16 gain_lut_hy[ACM_GAIN_LUT_HY_TOTAL_LENGTH]; + s16 gain_lut_hs[ACM_GAIN_LUT_HS_TOTAL_LENGTH]; + u16 y_gain; + u16 h_gain; + u16 s_gain; + u16 acm_enable; +}; + +struct post_csc { + u16 hue; + u16 saturation; + u16 contrast; + u16 brightness; + u16 r_gain; + u16 g_gain; + u16 b_gain; + u16 r_offset; + u16 g_offset; + u16 b_offset; + u16 csc_enable; +}; + struct rockchip_crtc_state { struct drm_crtc_state base; int vp_id; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 20aca17f7dce..9a1b1a608d80 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -686,11 +686,13 @@ static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode) static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode) { /* - * The default component order of serial rgb3x8 formats + * The default component order of serial formats * is BGR. So it is needed to enable RB swap. */ if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 || - bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8) + bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8 || + bus_format == MEDIA_BUS_FMT_RGB666_3X6 || + bus_format == MEDIA_BUS_FMT_RGB565_2X8_LE) return true; else return false; @@ -3268,12 +3270,14 @@ static void vop_dither_setup(struct drm_crtc *crtc) switch (s->bus_format) { case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_RGB565_2X8_LE: VOP_CTRL_SET(vop, dither_down_en, 1); VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB565); break; case MEDIA_BUS_FMT_RGB666_1X18: case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + case MEDIA_BUS_FMT_RGB666_3X6: VOP_CTRL_SET(vop, dither_down_en, 1); VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB666); break; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 167aa1f2a0e7..b37da66dd403 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -598,37 +598,6 @@ enum vop_hdr_format { HDR_FORMAT_MAX, }; -#define ACM_GAIN_LUT_HY_LENGTH (9*17) -#define ACM_GAIN_LUT_HY_TOTAL_LENGTH (ACM_GAIN_LUT_HY_LENGTH * 3) -#define ACM_GAIN_LUT_HS_LENGTH (13*17) -#define ACM_GAIN_LUT_HS_TOTAL_LENGTH (ACM_GAIN_LUT_HS_LENGTH * 3) -#define ACM_DELTA_LUT_H_LENGTH 65 -#define ACM_DELTA_LUT_H_TOTAL_LENGTH (ACM_DELTA_LUT_H_LENGTH * 3) - -struct post_acm { - s16 delta_lut_h[ACM_DELTA_LUT_H_TOTAL_LENGTH]; - s16 gain_lut_hy[ACM_GAIN_LUT_HY_TOTAL_LENGTH]; - s16 gain_lut_hs[ACM_GAIN_LUT_HS_TOTAL_LENGTH]; - u16 y_gain; - u16 h_gain; - u16 s_gain; - u16 acm_enable; -}; - -struct post_csc { - u16 hue; - u16 saturation; - u16 contrast; - u16 brightness; - u16 r_gain; - u16 g_gain; - u16 b_gain; - u16 r_offset; - u16 g_offset; - u16 b_offset; - u16 csc_enable; -}; - struct post_csc_coef { s32 csc_coef00; s32 csc_coef01; @@ -1463,7 +1432,9 @@ struct vop2_data { #define ROCKCHIP_OUT_MODE_P565 2 #define ROCKCHIP_OUT_MODE_BT656 5 #define ROCKCHIP_OUT_MODE_S888 8 +#define ROCKCHIP_OUT_MODE_S666 9 #define ROCKCHIP_OUT_MODE_YUV422 9 +#define ROCKCHIP_OUT_MODE_S565 10 #define ROCKCHIP_OUT_MODE_S888_DUMMY 12 #define ROCKCHIP_OUT_MODE_YUV420 14 /* for use special outface */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 865af5bac00e..c8e33eb00509 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -771,6 +771,9 @@ struct vop2_video_port { */ enum vop2_layer_phy_id primary_plane_phy_id; + struct post_acm acm_info; + struct post_csc csc_info; + /** * @refresh_rate_change: indicate whether refresh rate change */ @@ -9505,18 +9508,25 @@ static void vop3_post_acm_config(struct drm_crtc *crtc, struct post_acm *acm) u32 value; int i; - if (!acm) { - writel(0x2, vop2->acm_regs + RK3528_ACM_CTRL); - VOP_MODULE_SET(vop2, vp, acm_bypass_en, 1); + writel(0, vop2->acm_regs + RK3528_ACM_CTRL); + VOP_MODULE_SET(vop2, vp, acm_bypass_en, 0); + + if (!acm || !acm->acm_enable) return; - } - writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START); + /* + * If acm update parameters, it need disable acm in the first frame, + * then update parameters and enable acm in second frame. + */ + vop2_cfg_done(crtc); + readx_poll_timeout(readl, vop2->acm_regs + RK3528_ACM_CTRL, value, !value, 200, 50000); - value = (acm->acm_enable & 0x1) + ((adjusted_mode->hdisplay & 0xfff) << 8) + + value = RK3528_ACM_ENABLE + ((adjusted_mode->hdisplay & 0xfff) << 8) + ((adjusted_mode->vdisplay & 0xfff) << 20); writel(value, vop2->acm_regs + RK3528_ACM_CTRL); - VOP_MODULE_SET(vop2, vp, acm_bypass_en, acm->acm_enable ? 0 : 1); + + + writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START); value = (acm->y_gain & 0x3ff) + ((acm->h_gain << 10) & 0xffc00) + ((acm->s_gain << 20) & 0x3ff00000); @@ -9555,14 +9565,23 @@ static void vop3_post_acm_config(struct drm_crtc *crtc, struct post_acm *acm) static void vop3_post_config(struct drm_crtc *crtc) { struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); struct post_acm *acm; struct post_csc *csc; - acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL; - vop3_post_acm_config(crtc, acm); - csc = vcstate->post_csc_data ? (struct post_csc *)vcstate->post_csc_data->data : NULL; - vop3_post_csc_config(crtc, acm, csc); + if (csc && memcmp(&vp->csc_info, csc, sizeof(struct post_csc))) + memcpy(&vp->csc_info, csc, sizeof(struct post_csc)); + vop3_post_csc_config(crtc, &vp->acm_info, &vp->csc_info); + + acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL; + + if (acm && memcmp(&vp->acm_info, acm, sizeof(struct post_acm))) { + memcpy(&vp->acm_info, acm, sizeof(struct post_acm)); + vop3_post_acm_config(crtc, &vp->acm_info); + } else if (crtc->state->active_changed) { + vop3_post_acm_config(crtc, &vp->acm_info); + } } static void vop2_cfg_update(struct drm_crtc *crtc, @@ -9623,10 +9642,10 @@ static void vop2_cfg_update(struct drm_crtc *crtc, if (vp_data->feature & VOP_FEATURE_OVERSCAN) vop2_post_config(crtc); + spin_unlock(&vop2->reg_lock); + if (vp_data->feature & (VOP_FEATURE_POST_ACM | VOP_FEATURE_POST_CSC)) vop3_post_config(crtc); - - spin_unlock(&vop2->reg_lock); } static void vop2_sleep_scan_line_time(struct vop2_video_port *vp, int scan_line) diff --git a/drivers/gpu/drm/rockchip/rockchip_post_csc.c b/drivers/gpu/drm/rockchip/rockchip_post_csc.c index 7160be22a0cb..212a4b4b89e6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_post_csc.c +++ b/drivers/gpu/drm/rockchip/rockchip_post_csc.c @@ -5,7 +5,6 @@ */ #include "rockchip_post_csc.h" -#include "rockchip_drm_drv.h" #define PQ_CSC_HUE_TABLE_NUM 256 #define PQ_CSC_MODE_COEF_COMMENT_LEN 32 diff --git a/drivers/gpu/drm/rockchip/rockchip_post_csc.h b/drivers/gpu/drm/rockchip/rockchip_post_csc.h index 6c96211aaebc..1215a5cd1c87 100644 --- a/drivers/gpu/drm/rockchip/rockchip_post_csc.h +++ b/drivers/gpu/drm/rockchip/rockchip_post_csc.h @@ -9,6 +9,7 @@ #define _ROCKCHIP_POST_CSC_H #include +#include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" int rockchip_calc_post_csc(struct post_csc *csc, struct post_csc_coef *csc_coef, diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index 25188fda9efd..342a94ec7adb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -277,6 +277,15 @@ rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder, s->output_mode = ROCKCHIP_OUT_MODE_P565; s->output_if = VOP_OUTPUT_IF_RGB; break; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + s->output_mode = ROCKCHIP_OUT_MODE_S565; + s->output_if = VOP_OUTPUT_IF_RGB; + break; + case MEDIA_BUS_FMT_RGB666_3X6: + s->output_mode = ROCKCHIP_OUT_MODE_S666; + s->output_if = VOP_OUTPUT_IF_RGB; + break; case MEDIA_BUS_FMT_RGB888_3X8: case MEDIA_BUS_FMT_BGR888_3X8: s->output_mode = ROCKCHIP_OUT_MODE_S888; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index 56a34972b19b..a33f6fcecb78 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -1759,6 +1759,8 @@ /* RK3588 ACM register definition */ #define RK3528_ACM_CTRL 0x0000 +#define RK3528_ACM_ENABLE BIT(0) +#define RK3528_ACM_BYPASS BIT(1) #define RK3528_ACM_DELTA_RANGE 0x0004 #define RK3528_ACM_FETCH_START 0x0008 #define RK3528_ACM_DEBUG_POINT0 0x0010 diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index caabffdec145..25546630e84b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -5,6 +5,8 @@ menu "Misc devices" +source "drivers/misc/rk628/Kconfig" + config RK803 tristate "RK803" default n diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 28c27fbdd704..1306058114ab 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -3,6 +3,7 @@ # Makefile for misc devices that really don't fit anywhere else. # +obj-y += rk628/ obj-$(CONFIG_RK803) += rk803.o obj-y += rockchip/ obj-$(CONFIG_LT7911D_FB_NOTIFIER) += lt7911d-fb-notifier.o diff --git a/drivers/misc/rk628/Kconfig b/drivers/misc/rk628/Kconfig new file mode 100644 index 000000000000..7f89f18f42a1 --- /dev/null +++ b/drivers/misc/rk628/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "RK628 misc driver" +config RK628_MISC + tristate "rk628 misc driver" + default n + help + Say y here to enable Rockchip rk628 misc driver. + This option is used to support rgb/hdmi/bt1120 input and dsi/lvds/gvi/hdmi output. + +config RK628_MISC_HDMITX + bool "rk628 misc hdmitx driver" + default n + depends on RK628_MISC + depends on DRM + help + Say y here to enable Rockchip rk628 misc hdmitx driver. + This option is used to support hdmi output. + +config ROCKCHIP_THUNDER_BOOT_RK628 + bool "Rockchip RK628 Thunder Boot support" + default n + depends on RK628_MISC + help + Say y here to enable Rockchip rk628 thunder boot support. + This option make the kernel boot faster. + +endmenu diff --git a/drivers/misc/rk628/Makefile b/drivers/misc/rk628/Makefile new file mode 100644 index 000000000000..cb4285a06f89 --- /dev/null +++ b/drivers/misc/rk628/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +rk628_misc-$(CONFIG_RK628_MISC) += rk628.o rk628_cru.o rk628_config.o rk628_post_process.o \ + rk628_combrxphy.o rk628_hdmirx.o rk628_combtxphy.o rk628_dsi.o \ + panel.o rk628_lvds.o rk628_rgb.o rk628_gvi.o rk628_pinctrl.o \ + rk628_csi.o + +rk628_misc-$(CONFIG_RK628_MISC_HDMITX) += rk628_hdmitx.o + +obj-$(CONFIG_RK628_MISC) += rk628_misc.o diff --git a/drivers/misc/rk628/panel.c b/drivers/misc/rk628/panel.c new file mode 100644 index 000000000000..a0a35030779f --- /dev/null +++ b/drivers/misc/rk628/panel.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Rockchip Electronics Co. Ltd. + * + * Author: Guochun Huang + */ + +#include "rk628.h" +#include +#include + +#include "panel.h" + +static int +dsi_panel_parse_cmds(const u8 *data, int blen, struct panel_cmds *pcmds) +{ + unsigned int len; + char *buf, *bp; + struct cmd_ctrl_hdr *dchdr; + int i, cnt; + + if (!pcmds) + return -EINVAL; + + buf = kmemdup(data, blen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* scan init commands */ + bp = buf; + len = blen; + cnt = 0; + while (len > sizeof(*dchdr)) { + dchdr = (struct cmd_ctrl_hdr *)bp; + + if (dchdr->dlen > len) { + pr_err("%s: error, len=%d", __func__, dchdr->dlen); + return -EINVAL; + } + + bp += sizeof(*dchdr); + len -= sizeof(*dchdr); + bp += dchdr->dlen; + len -= dchdr->dlen; + cnt++; + } + + if (len != 0) { + pr_err("%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen); + kfree(buf); + return -EINVAL; + } + + pcmds->cmds = kcalloc(cnt, sizeof(struct cmd_desc), GFP_KERNEL); + if (!pcmds->cmds) { + kfree(buf); + return -ENOMEM; + } + + pcmds->cmd_cnt = cnt; + pcmds->buf = buf; + pcmds->blen = blen; + + bp = buf; + len = blen; + for (i = 0; i < cnt; i++) { + dchdr = (struct cmd_ctrl_hdr *)bp; + len -= sizeof(*dchdr); + bp += sizeof(*dchdr); + pcmds->cmds[i].dchdr = *dchdr; + pcmds->cmds[i].payload = bp; + bp += dchdr->dlen; + len -= dchdr->dlen; + } + + return 0; +} + +static int dsi_panel_get_cmds(struct rk628 *rk628, struct device_node *dsi_np) +{ + struct device_node *np; + const void *data; + int len; + int ret, err; + + np = of_find_node_by_name(dsi_np, "rk628-panel"); + if (!np) + return -EINVAL; + + data = of_get_property(np, "panel-init-sequence", &len); + if (data) { + rk628->panel->on_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL); + if (!rk628->panel->on_cmds) + return -ENOMEM; + + err = dsi_panel_parse_cmds(data, len, rk628->panel->on_cmds); + if (err) { + dev_err(rk628->dev, "failed to parse dsi panel init sequence\n"); + ret = err; + goto init_err; + } + } + + data = of_get_property(np, "panel-exit-sequence", &len); + if (data) { + rk628->panel->off_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL); + if (!rk628->panel->off_cmds) { + ret = -ENOMEM; + goto on_err; + } + + err = dsi_panel_parse_cmds(data, len, rk628->panel->off_cmds); + if (err) { + dev_err(rk628->dev, "failed to parse dsi panel exit sequence\n"); + ret = err; + goto exit_err; + } + } + + return 0; + +exit_err: + kfree(rk628->panel->off_cmds); +on_err: + kfree(rk628->panel->on_cmds->cmds); + kfree(rk628->panel->on_cmds->buf); +init_err: + kfree(rk628->panel->on_cmds); + + return ret; +} + +int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np) +{ + struct panel_simple *panel; + struct device *dev = rk628->dev; + struct device_node *backlight; + int ret; + + panel = devm_kzalloc(dev, sizeof(struct panel_simple), GFP_KERNEL); + if (!panel) + return -ENOMEM; + + panel->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(panel->supply)) { + ret = PTR_ERR(panel->supply); + dev_err(dev, "failed to get power regulator: %d\n", ret); + return ret; + } + + panel->enable_gpio = devm_gpiod_get_optional(dev, "panel-enable", GPIOD_OUT_LOW); + if (IS_ERR(panel->enable_gpio)) { + ret = PTR_ERR(panel->enable_gpio); + dev_err(dev, "failed to request panel enable GPIO: %d\n", ret); + return ret; + } + + panel->reset_gpio = devm_gpiod_get_optional(dev, "panel-reset", GPIOD_OUT_LOW); + if (IS_ERR(panel->reset_gpio)) { + ret = PTR_ERR(panel->reset_gpio); + dev_err(dev, "failed to request panel reset GPIO: %d\n", ret); + return ret; + } + + backlight = of_parse_phandle(dev->of_node, "panel-backlight", 0); + if (backlight) { + panel->backlight = of_find_backlight_by_node(backlight); + of_node_put(backlight); + + if (!panel->backlight) { + dev_err(dev, "failed to find backlight\n"); + return -EPROBE_DEFER; + } + + } + + rk628->panel = panel; + + if (rk628->output_mode == OUTPUT_MODE_DSI) { + ret = dsi_panel_get_cmds(rk628, np); + if (ret) { + dev_err(dev, "failed to get cmds\n"); + return ret; + } + } + + return 0; +} + +void rk628_panel_prepare(struct rk628 *rk628) +{ + int ret; + + if (rk628->panel->supply) { + ret = regulator_enable(rk628->panel->supply); + if (ret) + dev_info(rk628->dev, "failed to enable panel power supply\n"); + } + + if (rk628->panel->enable_gpio) { + gpiod_set_value(rk628->panel->enable_gpio, 0); + mdelay(120); + gpiod_set_value(rk628->panel->enable_gpio, 1); + mdelay(120); + } + + if (rk628->panel->reset_gpio) { + gpiod_set_value(rk628->panel->reset_gpio, 0); + mdelay(120); + gpiod_set_value(rk628->panel->reset_gpio, 1); + mdelay(120); + gpiod_set_value(rk628->panel->reset_gpio, 0); + mdelay(120); + } +} + +void rk628_panel_enable(struct rk628 *rk628) +{ + if (rk628->panel->backlight) + backlight_enable(rk628->panel->backlight); +} + +void rk628_panel_unprepare(struct rk628 *rk628) +{ + + if (rk628->panel->reset_gpio) { + gpiod_set_value(rk628->panel->reset_gpio, 1); + mdelay(120); + } + + if (rk628->panel->enable_gpio) { + gpiod_set_value(rk628->panel->enable_gpio, 0); + mdelay(120); + } + + if (rk628->panel->supply) + regulator_disable(rk628->panel->supply); +} + +void rk628_panel_disable(struct rk628 *rk628) +{ + if (rk628->panel->backlight) + backlight_disable(rk628->panel->backlight); + +} diff --git a/drivers/misc/rk628/panel.h b/drivers/misc/rk628/panel.h new file mode 100644 index 000000000000..07a34cf3ee17 --- /dev/null +++ b/drivers/misc/rk628/panel.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2021 Rockchip Electronics Co. Ltd. + * + * Author: Guochun Huang + */ +#ifndef _PANEL_H +#define _PANEL_H + +#include "rk628.h" + +int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np); +void rk628_panel_prepare(struct rk628 *rk628); +void rk628_panel_enable(struct rk628 *rk628); +void rk628_panel_unprepare(struct rk628 *rk628); +void rk628_panel_disable(struct rk628 *rk628); +#endif + diff --git a/drivers/misc/rk628/rk628.c b/drivers/misc/rk628/rk628.c new file mode 100644 index 000000000000..15df730c8048 --- /dev/null +++ b/drivers/misc/rk628/rk628.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Rockchip Electronics Co. Ltd. + * + * Author: Wyon Bi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include