From 5b2d04b9029b2d17e7c3aa07cf9e221642adef71 Mon Sep 17 00:00:00 2001 From: Gopikrishnaiah Anandan Date: Wed, 25 May 2016 11:05:33 -0700 Subject: drm/msm: Add support for PCC in Crtc CRTC contains the dspp block which contains color processing modules. Change adds the pcc support to the CRTC. Change-Id: Ib4091d43d819ab4f20f16892277ccd1d21285c61 Signed-off-by: Gopikrishnaiah Anandan --- drivers/gpu/drm/msm/Makefile | 3 +- drivers/gpu/drm/msm/msm_drv.h | 3 + drivers/gpu/drm/msm/msm_prop.c | 4 +- drivers/gpu/drm/msm/sde/sde_color_processing.c | 640 +++++++++++++++++++++++++ drivers/gpu/drm/msm/sde/sde_color_processing.h | 72 +++ drivers/gpu/drm/msm/sde/sde_crtc.c | 23 +- drivers/gpu/drm/msm/sde/sde_crtc.h | 9 + drivers/gpu/drm/msm/sde/sde_encoder.c | 9 +- drivers/gpu/drm/msm/sde/sde_hw_dspp.c | 130 ++++- drivers/gpu/drm/msm/sde/sde_hw_dspp.h | 29 ++ drivers/gpu/drm/msm/sde/sde_hw_lm.c | 4 +- drivers/gpu/drm/msm/sde/sde_hw_lm.h | 6 +- drivers/gpu/drm/msm/sde/sde_hw_mdss.h | 10 + 13 files changed, 927 insertions(+), 15 deletions(-) create mode 100644 drivers/gpu/drm/msm/sde/sde_color_processing.c create mode 100644 drivers/gpu/drm/msm/sde/sde_color_processing.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index d805d526f27d..2eb52c4a9581 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -42,6 +42,7 @@ msm-y := \ sde/sde_kms.o \ sde/sde_plane.o \ sde/sde_connector.o \ + sde/sde_color_processing.o \ msm_atomic.o \ msm_drv.o \ msm_fb.o \ @@ -56,7 +57,7 @@ msm-y := \ msm_ringbuffer.o \ msm_evtlog.o \ sde_power_handle.o \ - msm_prop.o \ + msm_prop.o # use drm gpu driver only if qcom_kgsl driver not available ifneq ($(CONFIG_QCOM_KGSL),y) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index e047e9646782..30aea910bee8 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -298,6 +298,9 @@ struct msm_drm_private { struct drm_property *crtc_property[CRTC_PROP_COUNT]; struct drm_property *conn_property[CONNECTOR_PROP_COUNT]; + /* Color processing properties for the crtc */ + struct drm_property **cp_property; + /* VRAM carveout, used when no IOMMU: */ struct { unsigned long size; diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c index e5b7ad5a2896..90310fb6369e 100644 --- a/drivers/gpu/drm/msm/msm_prop.c +++ b/drivers/gpu/drm/msm/msm_prop.c @@ -350,7 +350,7 @@ int msm_property_atomic_set(struct msm_property_info *info, property_idx = msm_property_index(info, property); if (!info || (property_idx == -EINVAL) || !property_values) { - DRM_ERROR("invalid argument(s)\n"); + DRM_DEBUG("Invalid argument(s)\n"); } else { /* extra handling for incoming properties */ mutex_lock(&info->property_lock); @@ -399,7 +399,7 @@ int msm_property_atomic_get(struct msm_property_info *info, property_idx = msm_property_index(info, property); if (!info || (property_idx == -EINVAL) || !property_values || !val) { - DRM_ERROR("invalid argument(s)\n"); + DRM_DEBUG("Invalid argument(s)\n"); } else { mutex_lock(&info->property_lock); *val = property_values[property_idx]; diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c new file mode 100644 index 000000000000..ec7d282eb24e --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c @@ -0,0 +1,640 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include "sde_color_processing.h" +#include "sde_kms.h" +#include "sde_crtc.h" +#include "sde_hw_dspp.h" +#include "sde_hw_lm.h" + +struct sde_color_process_node { + u32 property_id; + u32 prop_flags; + u32 feature; + void *blob_ptr; + uint64_t prop_val; + const struct sde_pp_blk *pp_blk; + struct list_head feature_list; + struct list_head active_list; + struct list_head dirty_list; + void (*dspp_feature_op)(struct sde_hw_dspp *ctx, void *cfg); + void (*lm_feature_op)(struct sde_hw_mixer *mixer, void *cfg); +}; + +struct sde_cp_prop_attach { + struct drm_crtc *crtc; + struct drm_property *prop; + struct sde_color_process_node *prop_node; + const struct sde_pp_blk *pp_blk; + u32 feature; + void *ops; + uint64_t val; +}; + +enum { + /* Append new DSPP features before SDE_CP_CRTC_DSPP_MAX */ + /* DSPP Features start */ + SDE_CP_CRTC_DSPP_IGC, + SDE_CP_CRTC_DSPP_PCC, + SDE_CP_CRTC_DSPP_GC, + SDE_CP_CRTC_DSPP_HUE, + SDE_CP_CRTC_DSPP_SAT, + SDE_CP_CRTC_DSPP_VAL, + SDE_CP_CRTC_DSPP_CONT, + SDE_CP_CRTC_DSPP_MEMCOLOR, + SDE_CP_CRTC_DSPP_SIXZONE, + SDE_CP_CRTC_DSPP_GAMUT, + SDE_CP_CRTC_DSPP_DITHER, + SDE_CP_CRTC_DSPP_HIST, + SDE_CP_CRTC_DSPP_MAX, + /* DSPP features end */ + + /* Append new LM features before SDE_CP_CRTC_MAX_FEATURES */ + /* LM feature start*/ + SDE_CP_CRTC_LM_GC, + /* LM feature end*/ + + SDE_CP_CRTC_MAX_FEATURES, +}; + +#define INIT_PROP_ATTACH(p, crtc, prop, node, blk, feature, ops, val) \ + do { \ + (p)->crtc = crtc; \ + (p)->prop = prop; \ + (p)->prop_node = node; \ + (p)->pp_blk = blk; \ + (p)->feature = feature; \ + (p)->ops = ops; \ + (p)->val = val; \ + } while (0) + +static int sde_cp_disable_crtc_blob_property( + struct sde_color_process_node *prop_node) +{ + struct drm_property_blob *blob = prop_node->blob_ptr; + + if (!blob) + return -EINVAL; + drm_property_unreference_blob(blob); + prop_node->blob_ptr = NULL; + return 0; +} + +static int sde_cp_disable_crtc_property(struct drm_crtc *crtc, + struct drm_property *property, + struct sde_color_process_node *prop_node) +{ + int ret = 0; + + if (property->flags & DRM_MODE_PROP_BLOB) { + ret = sde_cp_disable_crtc_blob_property(prop_node); + } else if (property->flags & DRM_MODE_PROP_RANGE) { + prop_node->prop_val = 0; + ret = 0; + } + return ret; +} + +static int sde_cp_enable_crtc_blob_property(struct drm_crtc *crtc, + struct sde_color_process_node *prop_node, + uint64_t val) +{ + struct drm_property_blob *blob = NULL; + + /** + * For non-blob based properties add support to create a blob + * using the val and store the blob_ptr in prop_node. + */ + blob = drm_property_lookup_blob(crtc->dev, val); + if (!blob) { + DRM_ERROR("invalid blob id %lld\n", val); + return -EINVAL; + } + /* Release refernce to existing payload of the property */ + if (prop_node->blob_ptr) + drm_property_unreference_blob(prop_node->blob_ptr); + + prop_node->blob_ptr = blob; + return 0; +} + +static int sde_cp_enable_crtc_property(struct drm_crtc *crtc, + struct drm_property *property, + struct sde_color_process_node *prop_node, + uint64_t val) +{ + int ret = -EINVAL; + + if (property->flags & DRM_MODE_PROP_BLOB) + ret = sde_cp_enable_crtc_blob_property(crtc, prop_node, val); + else if (property->flags & DRM_MODE_PROP_RANGE) { + ret = 0; + prop_node->prop_val = val; + } + return ret; +} + +static int sde_cp_crtc_get_mixer_idx(struct sde_crtc *sde_crtc) +{ + if (sde_crtc->num_mixers) + return sde_crtc->mixers[0].hw_lm->idx; + else + return -EINVAL; +} + +static struct sde_kms *get_kms(struct drm_crtc *crtc) +{ + struct msm_drm_private *priv = crtc->dev->dev_private; + + return to_sde_kms(priv->kms); +} + +static void sde_cp_crtc_prop_attach(struct sde_cp_prop_attach *prop_attach) +{ + + struct sde_crtc *sde_crtc = to_sde_crtc(prop_attach->crtc); + + drm_object_attach_property(&prop_attach->crtc->base, + prop_attach->prop, prop_attach->val); + + INIT_LIST_HEAD(&prop_attach->prop_node->active_list); + INIT_LIST_HEAD(&prop_attach->prop_node->dirty_list); + + prop_attach->prop_node->property_id = prop_attach->prop->base.id; + prop_attach->prop_node->prop_flags = prop_attach->prop->flags; + prop_attach->prop_node->feature = prop_attach->feature; + prop_attach->prop_node->pp_blk = prop_attach->pp_blk; + + if (prop_attach->feature < SDE_CP_CRTC_DSPP_MAX) + prop_attach->prop_node->dspp_feature_op = prop_attach->ops; + else + prop_attach->prop_node->lm_feature_op = prop_attach->ops; + + list_add(&prop_attach->prop_node->feature_list, + &sde_crtc->feature_list); +} + +void sde_cp_crtc_init(struct drm_crtc *crtc) +{ + struct sde_crtc *sde_crtc = NULL; + + if (!crtc) { + DRM_ERROR("invalid crtc %pK\n", crtc); + return; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return; + } + + INIT_LIST_HEAD(&sde_crtc->active_list); + INIT_LIST_HEAD(&sde_crtc->dirty_list); + INIT_LIST_HEAD(&sde_crtc->feature_list); +} + +static void sde_cp_crtc_install_range_property(struct drm_crtc *crtc, + char *name, + const struct sde_pp_blk *pp_blk, + u32 feature, void *ops, + uint64_t min, uint64_t max, + uint64_t val) +{ + struct drm_property *prop; + struct sde_color_process_node *prop_node = NULL; + struct msm_drm_private *priv; + struct sde_cp_prop_attach prop_attach; + + if (feature >= SDE_CP_CRTC_MAX_FEATURES) { + DRM_ERROR("invalid feature %d max %d\n", feature, + SDE_CP_CRTC_MAX_FEATURES); + return; + } + + prop_node = kzalloc(sizeof(*prop_node), GFP_KERNEL); + if (!prop_node) + return; + + priv = crtc->dev->dev_private; + prop = priv->cp_property[feature]; + + if (!prop) { + prop = drm_property_create_range(crtc->dev, 0, name, min, max); + if (!prop) { + DRM_ERROR("property create failed: %s\n", name); + kfree(prop_node); + return; + } + priv->cp_property[feature] = prop; + } + + INIT_PROP_ATTACH(&prop_attach, crtc, prop, prop_node, pp_blk, + feature, ops, val); + + sde_cp_crtc_prop_attach(&prop_attach); +} + +static void sde_cp_crtc_create_blob_property(struct drm_crtc *crtc, char *name, + const struct sde_pp_blk *pp_blk, + u32 feature, void *ops) +{ + struct drm_property *prop; + struct sde_color_process_node *prop_node = NULL; + struct msm_drm_private *priv; + uint64_t val = 0; + struct sde_cp_prop_attach prop_attach; + + if (feature >= SDE_CP_CRTC_MAX_FEATURES) { + DRM_ERROR("invalid feature %d max %d\n", feature, + SDE_CP_CRTC_MAX_FEATURES); + return; + } + + prop_node = kzalloc(sizeof(*prop_node), GFP_KERNEL); + if (!prop_node) + return; + + priv = crtc->dev->dev_private; + prop = priv->cp_property[feature]; + + if (!prop) { + prop = drm_property_create(crtc->dev, + DRM_MODE_PROP_BLOB, name, 0); + if (!prop) { + DRM_ERROR("property create failed: %s\n", name); + kfree(prop_node); + return; + } + priv->cp_property[feature] = prop; + } + + INIT_PROP_ATTACH(&prop_attach, crtc, prop, prop_node, pp_blk, + feature, ops, val); + + sde_cp_crtc_prop_attach(&prop_attach); +} + + +static void sde_cp_crtc_setfeature(struct sde_color_process_node *prop_node, + struct sde_crtc *sde_crtc) +{ + u32 num_mixers = sde_crtc->num_mixers; + uint64_t val = 0; + struct drm_property_blob *blob = NULL; + struct sde_hw_cp_cfg hw_cfg; + int i = 0; + bool is_dspp = true; + + if (!prop_node->dspp_feature_op && !prop_node->lm_feature_op) { + DRM_ERROR("ops not set for dspp/lm\n"); + return; + } + + is_dspp = !prop_node->lm_feature_op; + memset(&hw_cfg, 0, sizeof(hw_cfg)); + if (prop_node->prop_flags & DRM_MODE_PROP_BLOB) { + blob = prop_node->blob_ptr; + if (blob) { + hw_cfg.len = blob->length; + hw_cfg.payload = blob->data; + } + } else if (prop_node->prop_flags & DRM_MODE_PROP_RANGE) { + val = prop_node->prop_val; + hw_cfg.len = sizeof(prop_node->prop_val); + hw_cfg.payload = &prop_node->prop_val; + } else { + DRM_ERROR("property type is not supported\n"); + return; + } + + for (i = 0; i < num_mixers; i++) { + if (is_dspp) { + if (!sde_crtc->mixers[i].hw_dspp) + continue; + prop_node->dspp_feature_op(sde_crtc->mixers[i].hw_dspp, + &hw_cfg); + } else { + if (!sde_crtc->mixers[i].hw_lm) + continue; + prop_node->lm_feature_op(sde_crtc->mixers[i].hw_lm, + &hw_cfg); + } + } + + if (blob || val) { + DRM_DEBUG_DRIVER("Add feature to active list %d\n", + prop_node->property_id); + list_add_tail(&prop_node->active_list, &sde_crtc->active_list); + } else { + DRM_DEBUG_DRIVER("remove feature from active list %d\n", + prop_node->property_id); + list_del_init(&prop_node->active_list); + } + /* Programming of feature done remove from dirty list */ + list_del_init(&prop_node->dirty_list); +} + +void sde_cp_crtc_apply_properties(struct drm_crtc *crtc) +{ + struct sde_crtc *sde_crtc = NULL; + bool set_dspp_flush = false, set_lm_flush = false; + struct sde_color_process_node *prop_node = NULL, *n = NULL; + struct sde_hw_ctl *ctl; + uint32_t flush_mask = 0; + u32 num_mixers = 0, i = 0; + + if (!crtc || !crtc->dev) { + DRM_ERROR("invalid crtc %pK dev %pK\n", crtc, + (crtc ? crtc->dev : NULL)); + return; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return; + } + + num_mixers = sde_crtc->num_mixers; + if (!num_mixers) { + DRM_DEBUG_DRIVER("no mixers for this crtc\n"); + return; + } + + /* Check if dirty list is empty for early return */ + if (list_empty(&sde_crtc->dirty_list)) { + DRM_DEBUG_DRIVER("Dirty list is empty\n"); + return; + } + + list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list, + dirty_list) { + sde_cp_crtc_setfeature(prop_node, sde_crtc); + /* Set the flush flag to true */ + if (prop_node->dspp_feature_op) + set_dspp_flush = true; + else + set_lm_flush = true; + } + + for (i = 0; i < num_mixers; i++) { + ctl = sde_crtc->mixers[i].hw_ctl; + if (!ctl) + continue; + if (set_dspp_flush && ctl->ops.get_bitmask_dspp + && sde_crtc->mixers[i].hw_dspp) + ctl->ops.get_bitmask_dspp(ctl, + &flush_mask, + sde_crtc->mixers[i].hw_dspp->idx); + ctl->ops.update_pending_flush(ctl, flush_mask); + if (set_lm_flush && ctl->ops.get_bitmask_mixer + && sde_crtc->mixers[i].hw_lm) + flush_mask = ctl->ops.get_bitmask_mixer(ctl, + sde_crtc->mixers[i].hw_lm->idx); + ctl->ops.update_pending_flush(ctl, flush_mask); + } +} + +void sde_cp_crtc_install_properties(struct drm_crtc *crtc) +{ + struct sde_kms *kms = NULL; + struct sde_crtc *sde_crtc = NULL; + struct sde_mdss_cfg *catalog = NULL; + unsigned long features = 0; + int idx = 0, i = 0; + char feature_name[256]; + struct msm_drm_private *priv; + struct sde_hw_dspp *hw_dspp = NULL; + struct sde_hw_mixer *hw_mixer = NULL; + + if (!crtc || !crtc->dev || !crtc->dev->dev_private) { + DRM_ERROR("invalid crtc %pK dev %pK\n", + crtc, ((crtc) ? crtc->dev : NULL)); + return; + } + + sde_crtc = to_sde_crtc(crtc); + kms = get_kms(crtc); + if (!kms || !kms->catalog || !sde_crtc) { + DRM_ERROR("invalid sde kms %pK catalog %pK sde_crtc %pK\n", + kms, ((kms) ? kms->catalog : NULL), sde_crtc); + return; + } + + /** + * Function can be called during the atomic_check with test_only flag + * and actual commit. Allocate properties only if feature list is + * empty during the atomic_check with test_only flag. + */ + if (!list_empty(&sde_crtc->feature_list)) + return; + + catalog = kms->catalog; + idx = sde_cp_crtc_get_mixer_idx(sde_crtc); + if (idx < 0 || idx >= catalog->mixer_count) { + DRM_ERROR("invalid idx %d\n", idx); + return; + } + + priv = crtc->dev->dev_private; + /** + * DSPP/LM properties are global to all the CRTCS. + * Properties are created for first CRTC and re-used for later + * crtcs. + */ + if (!priv->cp_property) + priv->cp_property = kzalloc((sizeof(priv->cp_property) * + SDE_CP_CRTC_MAX_FEATURES), GFP_KERNEL); + if (!priv->cp_property) + return; + memset(feature_name, 0, sizeof(feature_name)); + + if (idx >= catalog->dspp_count) + goto lm_property; + + /* Check for all the DSPP properties and attach it to CRTC */ + hw_dspp = sde_crtc->mixers[0].hw_dspp; + features = (hw_dspp) ? hw_dspp->cap->features : 0; + + if (!hw_dspp || !hw_dspp->cap->sblk) + goto lm_property; + + for (i = 0; i < SDE_DSPP_MAX; i++) { + if (!test_bit(i, &features)) + continue; + switch (i) { + case SDE_DSPP_PCC: + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", + "SDE_DSPP_PCC_V", + (hw_dspp->cap->sblk->pcc.version >> 16)); + sde_cp_crtc_create_blob_property(crtc, feature_name, + &hw_dspp->cap->sblk->pcc, + SDE_CP_CRTC_DSPP_PCC, + hw_dspp->ops.setup_pcc); + break; + case SDE_DSPP_HSIC: + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", + "SDE_DSPP_HUE_V", + (hw_dspp->cap->sblk->hsic.version >> 16)); + sde_cp_crtc_install_range_property(crtc, feature_name, + &hw_dspp->cap->sblk->hsic, + SDE_CP_CRTC_DSPP_HUE, hw_dspp->ops.setup_hue, + 0, U32_MAX, 0); + break; + default: + break; + } + } + +lm_property: + /* Check for all the LM properties and attach it to CRTC */ + hw_mixer = sde_crtc->mixers[0].hw_lm; + features = (hw_mixer) ? hw_mixer->cap->features : 0; + + if (!hw_mixer || !hw_mixer->cap->sblk) + return; + + for (i = 0; i < SDE_MIXER_MAX; i++) { + if (!test_bit(i, &features)) + continue; + switch (i) { + case SDE_MIXER_GC: + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", + "SDE_LM_GC_V", + (hw_mixer->cap->sblk->gc.version >> 16)); + sde_cp_crtc_create_blob_property(crtc, feature_name, + &hw_mixer->cap->sblk->gc, + SDE_CP_CRTC_LM_GC, + hw_mixer->ops.setup_gc); + break; + default: + break; + } + } +} + +int sde_cp_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + struct sde_color_process_node *prop_node = NULL; + struct sde_crtc *sde_crtc = NULL; + int ret = 0; + u8 found = 0; + + if (!crtc || !property) { + DRM_ERROR("invalid crtc %pK property %pK\n", crtc, property); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return -EINVAL; + } + + list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) { + if (property->base.id == prop_node->property_id) { + found = 1; + break; + } + } + + if (!found) + return 0; + + /* remove the property from dirty list */ + list_del_init(&prop_node->dirty_list); + + if (!val) + ret = sde_cp_disable_crtc_property(crtc, property, prop_node); + else + ret = sde_cp_enable_crtc_property(crtc, property, + prop_node, val); + + if (!ret) { + /* remove the property from active list */ + list_del_init(&prop_node->active_list); + /* Mark the feature as dirty */ + list_add_tail(&prop_node->dirty_list, &sde_crtc->dirty_list); + } + return ret; +} + +int sde_cp_crtc_get_property(struct drm_crtc *crtc, + struct drm_property *property, uint64_t *val) +{ + struct sde_color_process_node *prop_node = NULL; + struct sde_crtc *sde_crtc = NULL; + int ret = -EINVAL; + + if (!crtc || !property || !val) { + DRM_ERROR("invalid crtc %pK property %pK val %pK\n", + crtc, property, val); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return -EINVAL; + } + + list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) { + if (property->base.id == prop_node->property_id) { + *val = prop_node->prop_val; + ret = 0; + break; + } + } + return ret; +} + +void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc) +{ + struct sde_crtc *sde_crtc = NULL; + struct sde_color_process_node *prop_node = NULL, *n = NULL; + + if (!crtc) { + DRM_ERROR("invalid crtc %pK\n", crtc); + return; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return; + } + + list_for_each_entry_safe(prop_node, n, &sde_crtc->feature_list, + feature_list) { + if (prop_node->prop_flags & DRM_MODE_PROP_BLOB + && prop_node->blob_ptr) + drm_property_unreference_blob(prop_node->blob_ptr); + + list_del_init(&prop_node->active_list); + list_del_init(&prop_node->dirty_list); + list_del_init(&prop_node->feature_list); + kfree(prop_node); + } + + INIT_LIST_HEAD(&sde_crtc->active_list); + INIT_LIST_HEAD(&sde_crtc->dirty_list); + INIT_LIST_HEAD(&sde_crtc->feature_list); +} diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h new file mode 100644 index 000000000000..d677c82ec931 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h @@ -0,0 +1,72 @@ + +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _SDE_COLOR_PROCESSING_H +#define _SDE_COLOR_PROCESSING_H +#include + +/** + * sde_cp_crtc_init(): Initialize color processing lists for a crtc. + * Should be called during crtc initialization. + * @crtc: Pointer to sde_crtc. + */ +void sde_cp_crtc_init(struct drm_crtc *crtc); + +/** + * sde_cp_crtc_install_properties(): Installs the color processing + * properties for a crtc. + * Should be called during crtc initialization. + * @crtc: Pointer to crtc. + */ +void sde_cp_crtc_install_properties(struct drm_crtc *crtc); + +/** + * sde_cp_crtc_destroy_properties: Destroys color processing + * properties for a crtc. + * should be called during crtc de-initialization. + * @crtc: Pointer to crtc. + */ +void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc); + +/** + * sde_cp_crtc_set_property: Set a color processing property + * for a crtc. + * Should be during atomic set property. + * @crtc: Pointer to crtc. + * @property: Property that needs to enabled/disabled. + * @val: Value of property. + */ +int sde_cp_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, uint64_t val); + +/** + * sde_cp_crtc_apply_properties: Enable/disable properties + * for a crtc. + * Should be called during atomic commit call. + * @crtc: Pointer to crtc. + */ +void sde_cp_crtc_apply_properties(struct drm_crtc *crtc); + +/** + * sde_cp_crtc_get_property: Get value of color processing property + * for a crtc. + * Should be during atomic get property. + * @crtc: Pointer to crtc. + * @property: Property that needs to enabled/disabled. + * @val: Value of property. + * + */ +int sde_cp_crtc_get_property(struct drm_crtc *crtc, + struct drm_property *property, uint64_t *val); +#endif /*_SDE_COLOR_PROCESSING_H */ diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index f8beea56ae9a..366c97e9c5c8 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -24,6 +24,7 @@ #include "sde_hw_lm.h" #include "sde_hw_ctl.h" #include "sde_crtc.h" +#include "sde_color_processing.h" #define CTL(i) (CTL_0 + (i)) #define LM(i) (LM_0 + (i)) @@ -63,6 +64,7 @@ static void sde_crtc_destroy(struct drm_crtc *crtc) return; msm_property_destroy(&sde_crtc->property_info); + sde_cp_crtc_destroy_properties(crtc); debugfs_remove_recursive(sde_crtc->debugfs_root); sde_fence_deinit(&sde_crtc->output_fence); @@ -494,10 +496,11 @@ static void _sde_crtc_setup_mixer_for_encoder( struct sde_crtc_mixer *mixer; struct sde_hw_ctl *last_valid_ctl = NULL; int i; - struct sde_rm_hw_iter lm_iter, ctl_iter; + struct sde_rm_hw_iter lm_iter, ctl_iter, dspp_iter; sde_rm_init_hw_iter(&lm_iter, enc->base.id, SDE_HW_BLK_LM); sde_rm_init_hw_iter(&ctl_iter, enc->base.id, SDE_HW_BLK_CTL); + sde_rm_init_hw_iter(&dspp_iter, enc->base.id, SDE_HW_BLK_DSPP); /* Set up all the mixers and ctls reserved by this encoder */ for (i = sde_crtc->num_mixers; i < ARRAY_SIZE(sde_crtc->mixers); i++) { @@ -524,6 +527,10 @@ static void _sde_crtc_setup_mixer_for_encoder( return; } + /* Dspp may be null */ + (void) sde_rm_get_hw(rm, &dspp_iter); + mixer->hw_dspp = (struct sde_hw_dspp *)dspp_iter.hw; + mixer->encoder = enc; sde_crtc->num_mixers++; @@ -597,6 +604,7 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, return; _sde_crtc_blend_setup(crtc); + sde_cp_crtc_apply_properties(crtc); /* * PP_DONE irq is only used by command mode for now. @@ -1025,7 +1033,12 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc, property); if (idx == CRTC_PROP_INPUT_FENCE_TIMEOUT) _sde_crtc_set_input_fence_timeout(cstate); + } else { + ret = sde_cp_crtc_set_property(crtc, + property, val); } + if (ret) + DRM_ERROR("failed to set the property\n"); } return ret; @@ -1081,9 +1094,13 @@ static int sde_crtc_atomic_get_property(struct drm_crtc *crtc, ret = msm_property_atomic_get(&sde_crtc->property_info, cstate->property_values, cstate->property_blobs, property, val); + if (ret) + ret = sde_cp_crtc_get_property(crtc, + property, val); } + if (ret) + DRM_ERROR("get property failed\n"); } - return ret; } @@ -1193,6 +1210,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, return ERR_PTR(-ENOMEM); crtc = &sde_crtc->base; + crtc->dev = dev; sde_crtc->drm_crtc_id = drm_crtc_id; atomic_set(&sde_crtc->drm_requested_vblank, 0); @@ -1218,6 +1236,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, sizeof(struct sde_crtc_state)); sde_crtc_install_properties(crtc); + sde_cp_crtc_init(crtc); SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name); return crtc; diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index cee9572b4589..4d26d3330c8e 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -28,6 +28,7 @@ * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC * @hw_lm: LM HW Driver context * @hw_ctl: CTL Path HW driver context + * @hw_dspp: DSPP HW driver context * @encoder: Encoder attached to this lm & ctl * @mixer_op_mode: mixer blending operation mode * @flush_mask: mixer flush mask for ctl, mixer and pipe @@ -35,6 +36,7 @@ struct sde_crtc_mixer { struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; + struct sde_hw_dspp *hw_dspp; struct drm_encoder *encoder; u32 mixer_op_mode; u32 flush_mask; @@ -58,6 +60,9 @@ struct sde_crtc_mixer { * @property_defaults : Array of default values for generic property support * @stage_cfg : H/w mixer stage configuration * @debugfs_root : Parent of debugfs node + * @feature_list : list of color processing features supported on a crtc + * @active_list : list of color processing features are active + * @dirty_list : list of color processing features are dirty */ struct sde_crtc { struct drm_crtc base; @@ -84,6 +89,10 @@ struct sde_crtc { struct sde_hw_stage_cfg stage_cfg; struct dentry *debugfs_root; + + struct list_head feature_list; + struct list_head active_list; + struct list_head dirty_list; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 69fbe3aa13ce..77a0b031793b 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -23,6 +23,7 @@ #include "sde_formats.h" #include "sde_encoder_phys.h" #include "display_manager.h" +#include "sde_color_processing.h" #define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\ (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) @@ -309,8 +310,11 @@ static int sde_encoder_virt_atomic_check( ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state, conn_state, true); - /* Call to populate mode->crtc* information required by framework */ - drm_mode_set_crtcinfo(adj_mode, 0); + if (!ret) { + sde_cp_crtc_install_properties(drm_enc->crtc); + /* populate mode->crtc* information required by framework */ + drm_mode_set_crtcinfo(adj_mode, 0); + } MSM_EVT(drm_enc->dev, adj_mode->flags, adj_mode->private_flags); @@ -437,6 +441,7 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) SDE_DEBUG_ENC(sde_enc, "cleared master\n"); bs_set(sde_enc, 0); + sde_cp_crtc_destroy_properties(drm_enc->crtc); sde_rm_release(&sde_kms->rm, drm_enc); } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c index 62f2ebbdd889..52b51ed360d8 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c @@ -9,12 +9,25 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - +#include "drm/msm_drm_pp.h" #include "sde_hw_mdss.h" #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_dspp.h" +#define PCC_ENABLE BIT(0) +#define PCC_OP_MODE_OFF 0 +#define PCC_CONST_COEFF_OFF 4 +#define PCC_R_COEFF_OFF 0x10 +#define PCC_G_COEFF_OFF 0x1C +#define PCC_B_COEFF_OFF 0x28 +#define PCC_RG_COEFF_OFF 0x34 +#define PCC_RB_COEFF_OFF 0x40 +#define PCC_GB_COEFF_OFF 0x4C +#define PCC_RGB_COEFF_OFF 0x58 +#define PCC_CONST_COEFF_MASK 0xFFFF +#define PCC_COEFF_MASK 0x3FFFF + static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, struct sde_mdss_cfg *m, void __iomem *addr, @@ -51,8 +64,96 @@ void sde_dspp_setup_pa(struct sde_hw_dspp *dspp, void *cfg) { } +void sde_dspp_setup_hue(struct sde_hw_dspp *dspp, void *cfg) +{ +} + void sde_dspp_setup_pcc(struct sde_hw_dspp *ctx, void *cfg) { + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_pcc *pcc; + void __iomem *base; + + if (!hw_cfg || (hw_cfg->len != sizeof(*pcc) && hw_cfg->payload)) { + DRM_ERROR( + "hw_cfg %pK payload %pK payload size %d exp size %zd\n", + hw_cfg, (hw_cfg ? hw_cfg->payload : NULL), + ((hw_cfg) ? hw_cfg->len : 0), sizeof(*pcc)); + return; + } + base = ctx->hw.base_off + ctx->cap->base; + + /* Turn off feature */ + if (!hw_cfg->payload) { + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base, + PCC_OP_MODE_OFF); + return; + } + DRM_DEBUG_DRIVER("Enable PCC feature\n"); + pcc = hw_cfg->payload; + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF, + pcc->r.c & PCC_CONST_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, + ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF + 4, + pcc->g.c & PCC_CONST_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, + ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF + 8, + pcc->b.c & PCC_CONST_COEFF_MASK); + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF, + pcc->r.r & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF + 4, + pcc->g.r & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF + 8, + pcc->b.r & PCC_COEFF_MASK); + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF, + pcc->r.g & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF + 4, + pcc->g.g & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF + 8, + pcc->b.g & PCC_COEFF_MASK); + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF, + pcc->r.b & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF + 4, + pcc->g.b & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF + 8, + pcc->b.b & PCC_COEFF_MASK); + + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF, + pcc->r.rg & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF + 4, + pcc->g.rg & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF + 8, + pcc->b.rg & PCC_COEFF_MASK); + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF, + pcc->r.rb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF + 4, + pcc->g.rb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF + 8, + pcc->b.rb & PCC_COEFF_MASK); + + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF, + pcc->r.gb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF + 4, + pcc->g.gb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF + 8, + pcc->b.gb & PCC_COEFF_MASK); + + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF, + pcc->r.rgb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, + ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF + 4, + pcc->g.rgb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, + ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF + 8, + pcc->b.rgb & PCC_COEFF_MASK); + SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base, PCC_ENABLE); } void sde_dspp_setup_sharpening(struct sde_hw_dspp *ctx, void *cfg) @@ -75,10 +176,31 @@ void sde_dspp_setup_dither(struct sde_hw_dspp *ctx, void *cfg) { } -static void _setup_dspp_ops(struct sde_hw_dspp_ops *ops, - unsigned long features) +static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) { + int i = 0; + + for (i = 0; i < SDE_DSPP_MAX; i++) { + if (!test_bit(i, &features)) + continue; + switch (i) { + case SDE_DSPP_PCC: + if (c->cap->sblk->pcc.version == + (SDE_COLOR_PROCESS_VER(0x1, 0x0))) + c->ops.setup_pcc = sde_dspp_setup_pcc; + break; + case SDE_DSPP_HSIC: + if (c->cap->sblk->hsic.version == + (SDE_COLOR_PROCESS_VER(0x1, 0x0))) + c->ops.setup_hue = sde_dspp_setup_hue; + break; + default: + break; + } + + } } + struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, void __iomem *addr, struct sde_mdss_cfg *m) @@ -99,7 +221,7 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, /* Assign ops */ c->idx = idx; c->cap = cfg; - _setup_dspp_ops(&c->ops, c->cap->features); + _setup_dspp_ops(c, c->cap->features); return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h index 6ba161a026ec..d65bb2253af2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h @@ -83,12 +83,41 @@ struct sde_hw_dspp_ops { * @cfg: Pointer to configuration */ void (*setup_danger_safe)(struct sde_hw_dspp *ctx, void *cfg); + /** * setup_dither - setup dspp dither * @ctx: Pointer to dspp context * @cfg: Pointer to configuration */ void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_hue - setup dspp PA hue + * @ctx: Pointer to dspp context + * @cfg: Pointer to configuration + */ + void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_sat - setup dspp PA saturation + * @ctx: Pointer to dspp context + * @cfg: Pointer to configuration + */ + void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_val - setup dspp PA value + * @ctx: Pointer to dspp context + * @cfg: Pointer to configuration + */ + void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_cont - setup dspp PA contrast + * @ctx: Pointer to dspp context + * @cfg: Pointer to configuration + */ + void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg); }; /** diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c index 5e268f3925c8..995eaf43085d 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c @@ -151,7 +151,7 @@ static void sde_hw_lm_setup_color3(struct sde_hw_mixer *ctx, SDE_REG_WRITE(c, LM_OP_MODE, op_mode); } -static void sde_hw_lm_gammacorrection(struct sde_hw_mixer *mixer, +static void sde_hw_lm_gc(struct sde_hw_mixer *mixer, void *cfg) { } @@ -167,7 +167,7 @@ static void _setup_mixer_ops(struct sde_mdss_cfg *m, ops->setup_blend_config = sde_hw_lm_setup_blend_config; ops->setup_alpha_out = sde_hw_lm_setup_color3; ops->setup_border_color = sde_hw_lm_setup_border_color; - ops->setup_gammcorrection = sde_hw_lm_gammacorrection; + ops->setup_gc = sde_hw_lm_gc; }; struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.h b/drivers/gpu/drm/msm/sde/sde_hw_lm.h index 1bde86eb3a4e..7318c18ddaba 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_lm.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.h @@ -60,8 +60,10 @@ struct sde_hw_lm_ops { void (*setup_border_color)(struct sde_hw_mixer *ctx, struct sde_mdss_color *color, u8 border_en); - - void (*setup_gammcorrection)(struct sde_hw_mixer *mixer, + /** + * setup_gc : enable/disable gamma correction feature + */ + void (*setup_gc)(struct sde_hw_mixer *mixer, void *cfg); }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index aaad5686a5a1..f4ed48ee5a61 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -424,4 +424,14 @@ struct sde_mdss_color { #define SDE_DBG_MASK_TOP (1 << 9) #define SDE_DBG_MASK_VBIF (1 << 10) +/** + * struct sde_hw_cp_cfg: hardware dspp/lm feature payload. + * @payload: Feature specific payload. + * @len: Length of the payload. + */ +struct sde_hw_cp_cfg { + void *payload; + u32 len; +}; + #endif /* _SDE_HW_MDSS_H */ -- cgit v1.2.3