| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include "meson_crtc.h" |
| #include "meson_vpu_pipeline.h" |
| #include <linux/init.h> |
| #include <linux/amlogic/gki_module.h> |
| |
| #include <linux/amlogic/media/vout/vout_notify.h> |
| #include <vout/vout_serve/vout_func.h> |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| #include <linux/amlogic/media/amvecm/amvecm.h> |
| #endif |
| #include <enhancement/amvecm/amcsc.h> |
| |
| #define EOTF_RESERVED 23 |
| |
| #define OSD_DUMP_PATH "/tmp/osd_dump/" |
| |
| int crtc_force_hint; |
| MODULE_PARM_DESC(crtc_force_hint, "\n force modesetting hint\n"); |
| module_param(crtc_force_hint, int, 0644); |
| |
| int gamma_ctl = 1; |
| |
| #ifndef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION |
| bool get_amdv_mode(void) |
| { |
| return false; |
| } |
| |
| bool is_amdv_enable(void) |
| { |
| return false; |
| } |
| |
| void set_amdv_ll_policy(int policy) |
| { |
| } |
| |
| void set_amdv_policy(int policy) |
| { |
| } |
| |
| void set_amdv_enable(bool enable) |
| { |
| } |
| |
| void set_amdv_mode(int mode) |
| { |
| } |
| #endif |
| |
| static int gamma_boot_ctl(char *str) |
| { |
| if (strncmp("0", str, 1) == 0) |
| gamma_ctl = 0; |
| else |
| gamma_ctl = 1; |
| |
| return 0; |
| } |
| |
| __setup("gamma=", gamma_boot_ctl); |
| |
| /* |
| **********TV SUPPORT DV**************** |
| *HDR 2 |
| *DV 18 |
| *SDR 0 |
| *DV LL 19 |
| */ |
| static void set_eotf_by_property(struct am_meson_crtc_state *state) |
| { |
| if (state->crtc_eotf_by_property_flag) { |
| if (!dv_support() && get_amdv_mode() && is_amdv_enable()) { |
| DRM_INFO("[%s] support DV\n", __func__); |
| if (state->eotf_type_by_property == 2) { |
| set_amdv_ll_policy(0); |
| set_amdv_policy(2); |
| set_amdv_enable(1); |
| set_amdv_mode(2); |
| } else if (state->eotf_type_by_property == 18) { |
| set_amdv_ll_policy(0); |
| set_amdv_policy(2); |
| set_amdv_enable(1); |
| set_amdv_mode(1); |
| } else if (state->eotf_type_by_property == 0) { |
| set_amdv_ll_policy(0); |
| set_amdv_policy(2); |
| set_amdv_enable(1); |
| set_amdv_mode(4); |
| } else if (state->eotf_type_by_property == 19) { |
| set_amdv_ll_policy(1); |
| set_amdv_policy(2); |
| set_amdv_enable(1); |
| set_amdv_mode(1); |
| |
| } |
| } else { |
| DRM_INFO("[%s] can not support DV\n", __func__); |
| if (state->eotf_type_by_property == 0) { |
| set_amdv_policy(2); |
| set_amdv_mode(0); |
| set_amdv_enable(0); |
| set_hdr_policy(2); |
| set_force_output(1); |
| } |
| if (state->eotf_type_by_property == 2) { |
| set_amdv_policy(2); |
| set_amdv_mode(0); |
| set_amdv_enable(0); |
| set_hdr_policy(2); |
| set_force_output(3); |
| } |
| } |
| } |
| } |
| |
| static void meson_crtc_destroy_state(struct drm_crtc *crtc, |
| struct drm_crtc_state *state) |
| { |
| struct am_meson_crtc_state *meson_crtc_state; |
| |
| meson_crtc_state = to_am_meson_crtc_state(state); |
| __drm_atomic_helper_crtc_destroy_state(&meson_crtc_state->base); |
| kfree(meson_crtc_state); |
| } |
| |
| static struct drm_crtc_state *meson_crtc_duplicate_state(struct drm_crtc *crtc) |
| { |
| struct am_meson_crtc_state *new_state, *cur_state; |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| |
| cur_state = to_am_meson_crtc_state(crtc->state); |
| |
| new_state = kzalloc(sizeof(*new_state), GFP_KERNEL); |
| if (!new_state) |
| return NULL; |
| |
| __drm_atomic_helper_crtc_duplicate_state(crtc, &new_state->base); |
| |
| new_state->crtc_hdr_process_policy = |
| cur_state->crtc_hdr_process_policy; |
| new_state->crtc_eotf_type = cur_state->crtc_eotf_type; |
| new_state->crtc_dv_enable = cur_state->crtc_dv_enable; |
| new_state->crtc_hdr_enable = cur_state->crtc_hdr_enable; |
| new_state->crtc_eotf_by_property_flag = cur_state->crtc_eotf_by_property_flag; |
| new_state->eotf_type_by_property = cur_state->eotf_type_by_property; |
| new_state->dv_mode = cur_state->dv_mode; |
| new_state->preset_vmode = VMODE_INVALID; |
| new_state->vmode = cur_state->vmode; |
| new_state->prev_vrefresh = cur_state->prev_vrefresh; |
| new_state->prev_height = cur_state->prev_height; |
| |
| /*reset dynamic info.*/ |
| if (amcrtc->priv->logo_show_done) |
| new_state->uboot_mode_init = 0; |
| else |
| new_state->uboot_mode_init = cur_state->uboot_mode_init; |
| |
| return &new_state->base; |
| } |
| |
| static void meson_crtc_init_hdr_preference |
| (struct am_meson_crtc_state *crtc_state) |
| { |
| crtc_state->crtc_hdr_process_policy = get_hdr_policy(); |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION |
| crtc_state->crtc_dv_enable = is_amdv_enable(); |
| #else |
| crtc_state->crtc_dv_enable = false; |
| #endif |
| |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM |
| crtc_state->crtc_hdr_enable = true; |
| #else |
| crtc_state->crtc_hdr_enable = false; |
| #endif |
| } |
| |
| static void meson_crtc_reset(struct drm_crtc *crtc) |
| { |
| struct am_meson_crtc_state *meson_crtc_state; |
| |
| if (crtc->state) { |
| meson_crtc_destroy_state(crtc, crtc->state); |
| crtc->state = NULL; |
| } |
| |
| meson_crtc_state = kzalloc(sizeof(*meson_crtc_state), GFP_KERNEL); |
| if (!meson_crtc_state) |
| return; |
| |
| crtc->state = &meson_crtc_state->base; |
| crtc->state->crtc = crtc; |
| |
| meson_crtc_init_hdr_preference(meson_crtc_state); |
| } |
| |
| static int get_osd_pixelformat(void) |
| { |
| int ret = 0; |
| |
| ret = BIT(RGBA_8888) | BIT(RGBX_8888) | BIT(RGB_888) | BIT(RGB_565) | BIT(BGRA_8888); |
| return ret; |
| } |
| |
| static int get_video_pixelformat(void) |
| { |
| int ret = 0; |
| |
| ret = BIT(YCBCR_422_SP) | BIT(YCBCR_422_I); |
| return ret; |
| } |
| static int meson_crtc_atomic_get_property(struct drm_crtc *crtc, |
| const struct drm_crtc_state *state, |
| struct drm_property *property, |
| uint64_t *val) |
| { |
| struct am_meson_crtc_state *crtc_state = |
| to_am_meson_crtc_state(state); |
| struct am_meson_crtc *meson_crtc = to_am_meson_crtc(crtc); |
| int ret = 0; |
| |
| if (!crtc_state->crtc_eotf_by_property_flag) |
| crtc_state->eotf_type_by_property = EOTF_RESERVED; |
| |
| if (property == meson_crtc->hdr_policy) { |
| *val = crtc_state->crtc_hdr_process_policy; |
| return 0; |
| } else if (property == meson_crtc->hdmi_eotf) { |
| *val = crtc_state->eotf_type_by_property; |
| return 0; |
| } else if (property == meson_crtc->dv_enable_property) { |
| *val = crtc_state->crtc_dv_enable; |
| return 0; |
| } else if (property == meson_crtc->bgcolor_property) { |
| *val = crtc_state->crtc_bgcolor; |
| return 0; |
| } else if (property == meson_crtc->dv_mode_property) { |
| *val = crtc_state->dv_mode; |
| return 0; |
| } else if (property == meson_crtc->osd_pixelformat_property) { |
| *val = get_osd_pixelformat(); |
| return 0; |
| } else if (property == meson_crtc->video_pixelformat_property) { |
| *val = get_video_pixelformat(); |
| return 0; |
| } |
| |
| return ret; |
| } |
| |
| static int meson_crtc_atomic_set_property(struct drm_crtc *crtc, |
| struct drm_crtc_state *state, |
| struct drm_property *property, |
| uint64_t val) |
| { |
| struct am_meson_crtc_state *crtc_state = |
| to_am_meson_crtc_state(state); |
| struct am_meson_crtc *meson_crtc = to_am_meson_crtc(crtc); |
| int ret = 0; |
| |
| if (property == meson_crtc->hdr_policy) { |
| crtc_state->crtc_hdr_process_policy = val; |
| return 0; |
| } else if (property == meson_crtc->hdmi_eotf) { |
| crtc_state->eotf_type_by_property = val; |
| crtc_state->crtc_eotf_by_property_flag = true; |
| return 0; |
| } else if (property == meson_crtc->dv_enable_property) { |
| crtc_state->crtc_dv_enable = val; |
| return 0; |
| } else if (property == meson_crtc->bgcolor_property) { |
| crtc_state->crtc_bgcolor = val; |
| return 0; |
| } else if (property == meson_crtc->dv_mode_property) { |
| crtc_state->dv_mode = val; |
| return 0; |
| } |
| |
| return ret; |
| } |
| |
| static void meson_crtc_atomic_print_state(struct drm_printer *p, |
| const struct drm_crtc_state *state) |
| { |
| struct am_meson_crtc_state *cstate = |
| container_of(state, struct am_meson_crtc_state, base); |
| struct am_meson_crtc *meson_crtc = to_am_meson_crtc(cstate->base.crtc); |
| struct meson_drm *priv = meson_crtc->priv; |
| struct meson_vpu_pipeline_state *mvps; |
| struct drm_private_state *obj_state; |
| |
| obj_state = priv->pipeline->obj.state; |
| if (!obj_state) { |
| DRM_ERROR("null pipeline obj state!\n"); |
| return; |
| } |
| |
| mvps = container_of(obj_state, struct meson_vpu_pipeline_state, obj); |
| if (!mvps) { |
| DRM_INFO("%s mvps is NULL!\n", __func__); |
| return; |
| } |
| |
| drm_printf(p, "\t\tvrr_enabled=%u\n", state->vrr_enabled); |
| drm_printf(p, "\t\tbrr_mode=%s\n", cstate->brr_mode); |
| drm_printf(p, "\t\tbrr=%u\n", cstate->brr); |
| drm_printf(p, "\t\tuboot_mode_init=%u\n", cstate->uboot_mode_init); |
| drm_printf(p, "\t\tcrtc_hdr_policy:[%u,%u]\n", |
| cstate->crtc_hdr_process_policy, |
| cstate->crtc_eotf_type); |
| |
| drm_printf(p, "\t\tdv-hdr core state:[%d,%d]\n", |
| cstate->crtc_dv_enable, |
| cstate->crtc_hdr_enable); |
| |
| drm_printf(p, "\tmeson vpu pipeline state:\n"); |
| drm_printf(p, "\t\tenable_blocks=%llu\n", |
| mvps->sub_states[meson_crtc->crtc_index].enable_blocks); |
| drm_printf(p, "\t\tnum_plane=%u\n", mvps->num_plane); |
| drm_printf(p, "\t\tnum_plane_video=%u\n", mvps->num_plane_video); |
| drm_printf(p, "\t\tglobal_afbc=%u\n", mvps->global_afbc); |
| } |
| |
| static const char * const pipe_crc_sources[] = {"vpp1", "NULL"}; |
| |
| static const char *const *meson_crtc_get_crc_sources(struct drm_crtc *crtc, |
| size_t *count) |
| { |
| *count = ARRAY_SIZE(pipe_crc_sources); |
| return pipe_crc_sources; |
| } |
| |
| static int meson_crc_parse_source(const char *source, bool *enabled) |
| { |
| int i; |
| int count = ARRAY_SIZE(pipe_crc_sources); |
| |
| if (!source) { |
| *enabled = false; |
| return 0; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if (!strcmp(pipe_crc_sources[i], source)) |
| break; |
| } |
| |
| if (i >= count) { |
| *enabled = false; |
| return -EINVAL; |
| } else if (!strcmp(pipe_crc_sources[i], "NULL")) { |
| *enabled = false; |
| } else { |
| *enabled = true; |
| } |
| |
| return 0; |
| } |
| |
| static int meson_crtc_set_crc_source(struct drm_crtc *crtc, const char *source) |
| { |
| bool enabled = false; |
| int ret = 0; |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| |
| ret = meson_crc_parse_source(source, &enabled); |
| amcrtc->vpp_crc_enable = enabled; |
| |
| return ret; |
| } |
| |
| static int meson_crtc_verify_crc_source(struct drm_crtc *crtc, |
| const char *source, size_t *values_cnt) |
| { |
| bool enabled; |
| |
| if (meson_crc_parse_source(source, &enabled) < 0) { |
| DRM_ERROR("unknown source %s\n", source); |
| return -EINVAL; |
| } |
| |
| *values_cnt = 1; |
| |
| return 0; |
| } |
| |
| static int meson_crtc_enable_vblank(struct drm_crtc *crtc) |
| { |
| return 0; |
| } |
| |
| static void meson_crtc_disable_vblank(struct drm_crtc *crtc) |
| { |
| } |
| |
| static int meson_crtc_late_register(struct drm_crtc *crtc) |
| { |
| if (IS_ENABLED(CONFIG_DEBUG_FS)) |
| meson_crtc_debugfs_late_init(crtc); |
| |
| return 0; |
| } |
| |
| static void meson_crtc_early_unregister(struct drm_crtc *crtc) |
| { |
| } |
| |
| static const struct drm_crtc_funcs am_meson_crtc_funcs = { |
| .atomic_destroy_state = meson_crtc_destroy_state, |
| .atomic_duplicate_state = meson_crtc_duplicate_state, |
| .destroy = drm_crtc_cleanup, |
| .page_flip = drm_atomic_helper_page_flip, |
| .reset = meson_crtc_reset, |
| .set_config = drm_atomic_helper_set_config, |
| .atomic_get_property = meson_crtc_atomic_get_property, |
| .atomic_set_property = meson_crtc_atomic_set_property, |
| .atomic_print_state = meson_crtc_atomic_print_state, |
| .get_crc_sources = meson_crtc_get_crc_sources, |
| .set_crc_source = meson_crtc_set_crc_source, |
| .verify_crc_source = meson_crtc_verify_crc_source, |
| .enable_vblank = meson_crtc_enable_vblank, |
| .disable_vblank = meson_crtc_disable_vblank, |
| .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
| .late_register = meson_crtc_late_register, |
| .early_unregister = meson_crtc_early_unregister, |
| }; |
| |
| static bool am_meson_crtc_mode_fixup(struct drm_crtc *crtc, |
| const struct drm_display_mode *mode, |
| struct drm_display_mode *adj_mode) |
| { |
| /* TODO: drm_calc_timestamping_constants() do framedur_ns /= 2, |
| * reset crtc info same as logical size, so we can get correct |
| * framedur_ns. |
| */ |
| drm_mode_set_crtcinfo(adj_mode, 0); |
| |
| return true; |
| } |
| |
| static void am_meson_crtc_atomic_enable(struct drm_crtc *crtc, |
| struct drm_atomic_state *old_atomic_state) |
| { |
| int ret; |
| char *name, *brr_name; |
| enum vmode_e mode; |
| struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; |
| int adjusted_vrefresh; |
| struct drm_crtc_state *old_crtc_state; |
| struct drm_display_mode *old_mode; |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| struct meson_vpu_pipeline *pipeline = amcrtc->pipeline; |
| struct am_meson_crtc_state *meson_crtc_state = |
| to_am_meson_crtc_state(crtc->state); |
| struct meson_drm *priv = amcrtc->priv; |
| int hdrpolicy = 0; |
| |
| DRM_DEBUG("%s[%d]:in\n", __func__, amcrtc->crtc_index); |
| old_crtc_state = drm_atomic_get_old_crtc_state(old_atomic_state, crtc); |
| if (!old_crtc_state) { |
| DRM_INFO("%s crtc state is NULL!\n", __func__); |
| return; |
| } |
| |
| old_mode = &old_crtc_state->adjusted_mode; |
| |
| if (!adjusted_mode) { |
| DRM_ERROR("meson_crtc_enable NULL mode failed.\n"); |
| return; |
| } |
| DRM_INFO("%s-[%d] in: new[%s], old[%s], vmode[%d-%d], uboot[%d]\n", |
| __func__, amcrtc->crtc_index, |
| adjusted_mode->name, old_mode->name, |
| meson_crtc_state->vmode, meson_crtc_state->preset_vmode, |
| meson_crtc_state->uboot_mode_init); |
| adjusted_vrefresh = drm_mode_vrefresh(adjusted_mode); |
| |
| if (!priv->compat_mode) { |
| /* update follow source/follow sink to hdr/dv core. |
| * drm didnot send hdmitx pkt, we just set policy to hdr core. |
| */ |
| if (meson_crtc_state->crtc_hdr_process_policy |
| == MESON_HDR_POLICY_FOLLOW_SOURCE || |
| meson_crtc_state->crtc_hdr_process_policy |
| == MESON_HDR_POLICY_FOLLOW_SINK) { |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION |
| /*enable/disable dv*/ |
| if (meson_crtc_state->crtc_dv_enable) { |
| if (meson_crtc_state->crtc_eotf_type |
| == HDMI_EOTF_MESON_DOLBYVISION_LL) { |
| set_amdv_ll_policy(1); |
| } else { |
| set_amdv_ll_policy(0); |
| } |
| set_amdv_enable(true); |
| } |
| #endif |
| |
| hdrpolicy = (meson_crtc_state->crtc_hdr_process_policy |
| == MESON_HDR_POLICY_FOLLOW_SINK) ? 0 : 1; |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM |
| set_hdr_policy(hdrpolicy); |
| #endif |
| } |
| /*force eotf by property*/ |
| set_eotf_by_property(meson_crtc_state); |
| } |
| |
| /*update mode*/ |
| name = adjusted_mode->name; |
| if (crtc->state->vrr_enabled) { |
| DRM_INFO("%s-[%d], adjust raw %s to brr %s\n", |
| __func__, amcrtc->crtc_index, |
| name, meson_crtc_state->brr_mode); |
| brr_name = meson_crtc_state->brr_mode; |
| if (meson_crtc_state->valid_brr) |
| name = brr_name; |
| } |
| |
| if (meson_crtc_state->preset_vmode == VMODE_INVALID) { |
| mode = vout_func_validate_vmode(amcrtc->vout_index, name, 0); |
| if (mode == VMODE_MAX) { |
| DRM_ERROR("crtc [%d]: no matched vout mode\n", amcrtc->crtc_index); |
| return; |
| } |
| } else { |
| mode = meson_crtc_state->preset_vmode; |
| if (mode != VMODE_DUMMY_ENCL) |
| DRM_ERROR("crtc [%d]: only support dummy.\n", amcrtc->crtc_index); |
| } |
| |
| DRM_INFO("%s-[%d]: enable mode %s final vmode %d\n", |
| __func__, amcrtc->crtc_index, name, mode); |
| |
| if (mode == VMODE_DUMMY_ENCL || |
| mode == VMODE_DUMMY_ENCI || |
| mode == VMODE_DUMMY_ENCP) { |
| ret = vout_func_set_current_vmode(amcrtc->vout_index, mode); |
| if (ret) |
| DRM_ERROR("crtc[%d]: new mode[%d] set error\n", |
| amcrtc->crtc_index, mode); |
| else |
| meson_vout_update_mode_name(amcrtc->vout_index, name, "dummy"); |
| } else { |
| if (meson_crtc_state->uboot_mode_init) |
| mode |= VMODE_INIT_BIT_MASK; |
| |
| if (crtc->state->vrr_enabled && |
| adjusted_mode->hdisplay == old_mode->hdisplay && |
| adjusted_mode->vdisplay == old_mode->vdisplay) { |
| set_vframe_rate_hint(adjusted_vrefresh * 100); |
| DRM_INFO("%s-[%d], vrr set crtc enable, %d\n", |
| __func__, amcrtc->crtc_index, |
| adjusted_vrefresh * 100); |
| drm_crtc_vblank_on(crtc); |
| return; |
| } |
| |
| vout_func_set_state(amcrtc->vout_index, mode); |
| vout_func_update_viu(amcrtc->vout_index); |
| } |
| |
| meson_crtc_state->vmode = mode; |
| |
| memcpy(&pipeline->subs[amcrtc->crtc_index].mode, adjusted_mode, |
| sizeof(struct drm_display_mode)); |
| |
| drm_crtc_vblank_on(crtc); |
| enable_irq(amcrtc->irq); |
| |
| DRM_DEBUG("%s-[%d]: out\n", __func__, amcrtc->crtc_index); |
| } |
| |
| static void am_meson_crtc_atomic_disable(struct drm_crtc *crtc, |
| struct drm_atomic_state *old_atomic_state) |
| { |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; |
| struct drm_crtc_state *old_crtc_state; |
| struct drm_display_mode *old_mode; |
| struct am_meson_crtc_state *meson_crtc_state = |
| to_am_meson_crtc_state(crtc->state); |
| enum vmode_e mode; |
| |
| DRM_INFO("%s-[%d]:in\n", __func__, amcrtc->crtc_index); |
| old_crtc_state = drm_atomic_get_old_crtc_state(old_atomic_state, crtc); |
| if (!old_crtc_state) { |
| DRM_INFO("%s crtc state is NULL!\n", __func__); |
| return; |
| } |
| |
| old_mode = &old_crtc_state->adjusted_mode; |
| drm_crtc_vblank_off(crtc); |
| |
| if (crtc->state->vrr_enabled && |
| adjusted_mode->hdisplay == old_mode->hdisplay && |
| adjusted_mode->vdisplay == old_mode->vdisplay) { |
| DRM_INFO("%s, vrr enable, skip crtc disable\n", __func__); |
| return; |
| } |
| |
| if (crtc->state->event && !crtc->state->active) { |
| spin_lock_irq(&crtc->dev->event_lock); |
| drm_crtc_send_vblank_event(crtc, crtc->state->event); |
| spin_unlock_irq(&crtc->dev->event_lock); |
| crtc->state->event = NULL; |
| } |
| disable_irq(amcrtc->irq); |
| |
| if ((meson_crtc_state->vmode & VMODE_MASK) == VMODE_LCD) { |
| DRM_INFO("%s[%d], lcd skip setting null vmode\n", __func__, |
| meson_crtc_state->vmode); |
| return; |
| } |
| |
| meson_crtc_state->vmode = VMODE_INVALID; |
| /* disable output by config null |
| * Todo: replace or delete it if have new method |
| */ |
| mode = vout_func_validate_vmode(amcrtc->vout_index, "null", 0); |
| if (mode == VMODE_MAX) { |
| DRM_ERROR("no matched vout mode\n"); |
| return; |
| } |
| vout_func_set_state(amcrtc->vout_index, mode); |
| DRM_DEBUG("%s:out\n", __func__); |
| } |
| |
| static int meson_crtc_atomic_check(struct drm_crtc *crtc, |
| struct drm_atomic_state *atomic_state) |
| { |
| int ret; |
| struct meson_vpu_pipeline_state *mvps; |
| struct meson_vpu_sub_pipeline_state *mvsps; |
| struct drm_display_mode *mode; |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| struct am_meson_crtc_state *cur_state = |
| to_am_meson_crtc_state(crtc->state); |
| struct meson_drm *priv = amcrtc->priv; |
| struct drm_crtc_state *crtc_state; |
| struct am_meson_crtc_state *new_state; |
| ret = 0; |
| |
| crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); |
| if (!crtc_state) { |
| DRM_INFO("%s crtc state is NULL!\n", __func__); |
| return -EINVAL; |
| } |
| |
| mvps = meson_vpu_pipeline_get_state(amcrtc->pipeline, crtc_state->state); |
| mvsps = &mvps->sub_states[crtc->index]; |
| mode = &crtc_state->mode; |
| if (mode->hdisplay > 4096 || mode->vdisplay > 2160) |
| mvsps->more_4k = 1; |
| else |
| mvsps->more_4k = 0; |
| |
| if (priv->vpu_data->slice_mode == 1 && drm_mode_vrefresh(mode) > 60) |
| mvsps->more_60 = 1; |
| else |
| mvsps->more_60 = 0; |
| |
| new_state = to_am_meson_crtc_state(crtc_state); |
| /*apply parameters need modeset.*/ |
| if (atomic_state->allow_modeset) { |
| /*apply state value not set from property.*/ |
| DRM_DEBUG_KMS("%s force modeset.\n", __func__); |
| if (crtc_force_hint > 0) { |
| crtc_state->mode_changed = true; |
| crtc_force_hint = 0; |
| } |
| |
| if (cur_state->crtc_dv_enable != new_state->crtc_dv_enable) |
| crtc_state->mode_changed = true; |
| |
| if (cur_state->eotf_type_by_property != new_state->eotf_type_by_property) |
| crtc_state->mode_changed = true; |
| |
| if (cur_state->dv_mode != new_state->dv_mode) |
| crtc_state->mode_changed = true; |
| } |
| |
| /*check plane-update*/ |
| ret = vpu_pipeline_check(amcrtc->pipeline, atomic_state); |
| |
| return ret; |
| } |
| |
| static void am_meson_crtc_atomic_begin(struct drm_crtc *crtc, |
| struct drm_atomic_state *old_atomic_state) |
| { |
| } |
| |
| static void am_meson_crtc_atomic_flush(struct drm_crtc *crtc, |
| struct drm_atomic_state *old_atomic_state) |
| { |
| struct drm_color_ctm *ctm; |
| struct drm_color_lut *lut; |
| struct meson_vpu_sub_pipeline *sub_pipe; |
| unsigned long flags; |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); |
| struct drm_crtc_state *old_crtc_state;// |
| struct meson_drm *priv = amcrtc->priv; |
| struct meson_vpu_pipeline *pipeline = amcrtc->pipeline; |
| struct am_meson_crtc_state *old_am_crtc_state; |
| struct am_meson_crtc_state *meson_crtc_state; |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| int gamma_lut_size = 0; |
| #endif |
| |
| int crtc_index = amcrtc->crtc_index; |
| |
| old_crtc_state = drm_atomic_get_old_crtc_state(old_atomic_state, crtc); |
| if (!old_crtc_state) { |
| DRM_INFO("%s state is NULL!\n", __func__); |
| return; |
| } |
| old_am_crtc_state = to_am_meson_crtc_state(old_crtc_state); |
| |
| sub_pipe = &pipeline->subs[crtc_index]; |
| meson_crtc_state = to_am_meson_crtc_state(crtc->state); |
| if (crtc->state->color_mgmt_changed) { |
| DRM_INFO("%s color_mgmt_changed!\n", __func__); |
| if (crtc->state->ctm) { |
| DRM_INFO("%s color_mgmt_changed 1!\n", __func__); |
| ctm = (struct drm_color_ctm *) |
| crtc->state->ctm->data; |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| am_meson_ctm_set(0, ctm); |
| #endif |
| } else { |
| DRM_DEBUG("%s Disable CTM!\n", __func__); |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| am_meson_ctm_disable(); |
| #endif |
| } |
| } |
| if (crtc->state->gamma_lut != priv->gamma_lut_blob && gamma_ctl) { |
| DRM_DEBUG("%s GAMMA LUT blob changed!\n", __func__); |
| drm_property_blob_put(priv->gamma_lut_blob); |
| priv->gamma_lut_blob = NULL; |
| if (crtc->state->gamma_lut) { |
| DRM_INFO("%s Set GAMMA\n", __func__); |
| priv->gamma_lut_blob = |
| drm_property_blob_get(crtc->state->gamma_lut); |
| lut = (struct drm_color_lut *) |
| crtc->state->gamma_lut->data; |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| gamma_lut_size = amvecm_drm_get_gamma_size(0); |
| amvecm_drm_gamma_set(0, lut, gamma_lut_size); |
| #endif |
| } else { |
| DRM_DEBUG("%s Disable GAMMA!\n", __func__); |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| amvecm_drm_gamma_disable(0); |
| #endif |
| } |
| } |
| vpu_pipeline_prepare_update(amcrtc->pipeline, |
| crtc->mode.vdisplay, drm_mode_vrefresh(&crtc->mode), crtc_index); |
| if (!meson_crtc_state->uboot_mode_init) { |
| vpu_osd_pipeline_update(sub_pipe, old_atomic_state); |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| vpu_pipeline_finish_update(pipeline, crtc_index); |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| } |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| if (crtc->state->event) { |
| if (drm_crtc_vblank_get(crtc) == 0) |
| drm_crtc_arm_vblank_event(crtc, crtc->state->event); |
| else |
| amcrtc->event = crtc->state->event; |
| |
| crtc->state->event = NULL; |
| } |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM |
| if (meson_crtc_state->crtc_hdr_process_policy != |
| old_am_crtc_state->crtc_hdr_process_policy) { |
| set_hdr_policy(meson_crtc_state->crtc_hdr_process_policy); |
| } |
| #endif |
| } |
| |
| bool am_meson_crtc_get_scanout_position(struct drm_crtc *crtc, |
| bool in_vblank_irq, int *vpos, int *hpos, |
| ktime_t *stime, ktime_t *etime, |
| const struct drm_display_mode *mode) |
| { |
| int ret = 0; |
| struct am_meson_crtc *am_crtc = to_am_meson_crtc(crtc); |
| |
| ret = am_crtc->get_scanout_position(am_crtc, |
| in_vblank_irq, vpos, hpos, stime, etime, mode); |
| |
| return (ret == 0) ? true : false; |
| } |
| |
| static const struct drm_crtc_helper_funcs am_crtc_helper_funcs = { |
| .atomic_check = meson_crtc_atomic_check, |
| .mode_fixup = am_meson_crtc_mode_fixup, |
| .atomic_begin = am_meson_crtc_atomic_begin, |
| .atomic_flush = am_meson_crtc_atomic_flush, |
| .atomic_enable = am_meson_crtc_atomic_enable, |
| .atomic_disable = am_meson_crtc_atomic_disable, |
| .get_scanout_position = am_meson_crtc_get_scanout_position, |
| }; |
| |
| static int meson_crtc_get_scanout_position(struct am_meson_crtc *crtc, |
| bool in_vblank_irq, int *vpos, int *hpos, |
| ktime_t *stime, ktime_t *etime, |
| const struct drm_display_mode *mode) |
| { |
| int ret = 0; |
| /*adjust mode crtc_vtotal is same as logical vtotal*/ |
| int real_vtotal = 0; |
| |
| if (stime) |
| *stime = ktime_get(); |
| |
| ret = vpu_pipeline_read_scanout_pos(crtc->pipeline, vpos, hpos, crtc->crtc_index); |
| |
| if (mode->flags & DRM_MODE_FLAG_INTERLACE) { |
| /* for interlace mode, enc 0 ~ vtotal*2 |
| * include two interlace image. |
| */ |
| real_vtotal = mode->crtc_vtotal >> 1; |
| if (*vpos >= real_vtotal) |
| *vpos -= real_vtotal; |
| } |
| |
| if (etime) |
| *etime = ktime_get(); |
| |
| return ret; |
| } |
| |
| /* Optional eotf properties. */ |
| static const struct drm_prop_enum_list hdmi_eotf_enum_list[] = { |
| { HDMI_EOTF_TRADITIONAL_GAMMA_SDR, "SDR" }, |
| { HDMI_EOTF_SMPTE_ST2084, "HDR" }, |
| { HDMI_EOTF_MESON_DOLBYVISION, "HDMI_EOTF_MESON_DOLBYVISION" }, |
| { HDMI_EOTF_MESON_DOLBYVISION_LL, "HDMI_EOTF_MESON_DOLBYVISION_LL" }, |
| { EOTF_RESERVED, "EOTF_RESERVED" } |
| }; |
| |
| static void meson_crtc_init_hdmi_eotf_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_enum(drm_dev, 0, "EOTF", |
| hdmi_eotf_enum_list, |
| ARRAY_SIZE(hdmi_eotf_enum_list)); |
| if (prop) { |
| amcrtc->hdmi_eotf = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, EOTF_RESERVED); |
| } else { |
| DRM_ERROR("Failed to EOTF property\n"); |
| } |
| } |
| |
| static void meson_crtc_init_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_bool(drm_dev, 0, "meson.crtc.hdr_policy"); |
| if (prop) { |
| amcrtc->hdr_policy = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to UPDATE property\n"); |
| } |
| } |
| |
| static void meson_crtc_init_dv_enable_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_bool(drm_dev, 0, "dv_enable"); |
| if (prop) { |
| amcrtc->dv_enable_property = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to dv_enable property\n"); |
| } |
| } |
| |
| static void meson_crtc_init_dv_mode_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_bool(drm_dev, 0, "dv_mode"); |
| if (prop) { |
| amcrtc->dv_mode_property = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to dv_mode property\n"); |
| } |
| } |
| |
| static void meson_crtc_add_bgcolor_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_range(drm_dev, 0, "BACKGROUND_COLOR", |
| 0, GENMASK_ULL(63, 0)); |
| if (prop) { |
| amcrtc->bgcolor_property = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to background color property\n"); |
| } |
| } |
| |
| static void meson_crtc_init_video_pixelformat_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_range(drm_dev, 0, "video_pixelformat", |
| 0, 65535); |
| if (prop) { |
| amcrtc->video_pixelformat_property = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to video pixelformatcolor property\n"); |
| } |
| } |
| |
| static void meson_crtc_init_osd_pixelformat_property(struct drm_device *drm_dev, |
| struct am_meson_crtc *amcrtc) |
| { |
| struct drm_property *prop; |
| |
| prop = drm_property_create_range(drm_dev, 0, "osd_pixelformat", |
| 0, 65535); |
| if (prop) { |
| amcrtc->osd_pixelformat_property = prop; |
| drm_object_attach_property(&amcrtc->base.base, prop, 0); |
| } else { |
| DRM_ERROR("Failed to osd pixelformatcolor property\n"); |
| } |
| } |
| |
| struct am_meson_crtc *meson_crtc_bind(struct meson_drm *priv, int idx) |
| { |
| struct am_meson_crtc *amcrtc; |
| struct drm_crtc *crtc; |
| struct meson_vpu_pipeline *pipeline = priv->pipeline; |
| struct drm_plane *primary_plane; |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| int gamma_lut_size = 0; |
| #endif |
| int ret, plane_index; |
| char crtc_name[64]; |
| |
| DRM_DEBUG("%s[%d]\n", __func__, __LINE__); |
| |
| amcrtc = devm_kzalloc(priv->dev, sizeof(*amcrtc), GFP_KERNEL); |
| if (!amcrtc) |
| return NULL; |
| |
| amcrtc->priv = priv; |
| amcrtc->dev = priv->dev; |
| amcrtc->drm_dev = priv->drm; |
| amcrtc->crtc_index = idx; |
| amcrtc->vout_index = idx + 1;/*vout index start from 1.*/ |
| crtc = &amcrtc->base; |
| plane_index = priv->primary_plane_index[idx]; |
| primary_plane = &priv->osd_planes[plane_index]->base; |
| |
| snprintf(crtc_name, 64, "%s-%d", "VPP", amcrtc->crtc_index); |
| |
| ret = drm_crtc_init_with_planes(priv->drm, crtc, |
| primary_plane, priv->cursor_plane, |
| &am_meson_crtc_funcs, crtc_name); |
| if (ret) { |
| dev_err(amcrtc->drm_dev->dev, "Failed to init CRTC\n"); |
| return NULL; |
| } |
| |
| drm_crtc_helper_add(crtc, &am_crtc_helper_funcs); |
| #ifdef CONFIG_AMLOGIC_MEDIA_RDMA |
| meson_vpu_reg_handle_register(amcrtc->crtc_index); |
| #endif |
| #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT |
| if (gamma_ctl) { |
| amvecm_drm_init(0); |
| gamma_lut_size = amvecm_drm_get_gamma_size(0); |
| drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); |
| drm_crtc_enable_color_mgmt(crtc, 0, true, gamma_lut_size); |
| } |
| #endif |
| |
| amcrtc->get_scanout_position = meson_crtc_get_scanout_position; |
| amcrtc->force_crc_chk = 8; |
| atomic_set(&amcrtc->commit_num, 0); |
| meson_crtc_init_property(priv->drm, amcrtc); |
| meson_crtc_init_hdmi_eotf_property(priv->drm, amcrtc); |
| meson_crtc_init_dv_enable_property(priv->drm, amcrtc); |
| meson_crtc_init_dv_mode_property(priv->drm, amcrtc); |
| meson_crtc_add_bgcolor_property(priv->drm, amcrtc); |
| meson_crtc_init_osd_pixelformat_property(priv->drm, amcrtc); |
| meson_crtc_init_video_pixelformat_property(priv->drm, amcrtc); |
| amcrtc->pipeline = pipeline; |
| strcpy(amcrtc->osddump_path, OSD_DUMP_PATH); |
| priv->crtcs[priv->num_crtcs++] = amcrtc; |
| |
| return amcrtc; |
| } |
| |