Loading arch/arm64/boot/dts/qcom/sdm845-sde.dtsi +1 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ qcom,sde-dspp-gamut = <0x1000 0x00040000>; qcom,sde-dspp-pcc = <0x1700 0x00040000>; qcom,sde-dspp-gc = <0x17c0 0x00010008>; qcom,sde-dspp-hist = <0x800 0x00010007>; }; qcom,platform-supply-entries { Loading drivers/gpu/drm/msm/sde/sde_color_processing.c +365 −6 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ static void dspp_gc_install_property(struct drm_crtc *crtc); static void dspp_igc_install_property(struct drm_crtc *crtc); static void dspp_hist_install_property(struct drm_crtc *crtc); typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc); static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX]; Loading @@ -77,6 +79,8 @@ static void sde_cp_notify_ad_event(struct drm_crtc *crtc_drm, void *arg); static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc, enum ad_property ad_prop); static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg); #define setup_dspp_prop_install_funcs(func) \ do { \ func[SDE_DSPP_PCC] = dspp_pcc_install_property; \ Loading @@ -86,6 +90,7 @@ do { \ func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \ func[SDE_DSPP_GC] = dspp_gc_install_property; \ func[SDE_DSPP_IGC] = dspp_igc_install_property; \ func[SDE_DSPP_HIST] = dspp_hist_install_property; \ } while (0) typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc); Loading @@ -111,7 +116,8 @@ enum { SDE_CP_CRTC_DSPP_SIXZONE, SDE_CP_CRTC_DSPP_GAMUT, SDE_CP_CRTC_DSPP_DITHER, SDE_CP_CRTC_DSPP_HIST, SDE_CP_CRTC_DSPP_HIST_CTRL, SDE_CP_CRTC_DSPP_HIST_IRQ, SDE_CP_CRTC_DSPP_AD, SDE_CP_CRTC_DSPP_VLUT, SDE_CP_CRTC_DSPP_AD_MODE, Loading Loading @@ -365,6 +371,12 @@ void sde_cp_crtc_init(struct drm_crtc *crtc) return; } /* create blob to store histogram data */ sde_crtc->hist_blob = drm_property_create_blob(crtc->dev, sizeof(struct drm_msm_hist), NULL); if (IS_ERR(sde_crtc->hist_blob)) sde_crtc->hist_blob = NULL; mutex_init(&sde_crtc->crtc_cp_lock); INIT_LIST_HEAD(&sde_crtc->active_list); INIT_LIST_HEAD(&sde_crtc->dirty_list); Loading Loading @@ -532,6 +544,77 @@ static void sde_cp_crtc_install_enum_property(struct drm_crtc *crtc, sde_cp_crtc_prop_attach(&prop_attach); } static struct sde_crtc_irq_info *_sde_cp_get_intr_node(u32 event, struct sde_crtc *sde_crtc) { bool found = false; struct sde_crtc_irq_info *node = NULL; list_for_each_entry(node, &sde_crtc->user_event_list, list) { if (node->event == event) { found = true; break; } } if (!found) node = NULL; return node; } static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc) { struct drm_crtc *crtc_drm = &sde_crtc->base; struct sde_kms *kms = NULL; struct sde_hw_mixer *hw_lm; struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc_irq_info *node = NULL; int i, irq_idx, ret = 0; unsigned long flags; if (!crtc_drm) { DRM_ERROR("invalid crtc %pK\n", crtc_drm); return; } kms = get_kms(crtc_drm); for (i = 0; i < sde_crtc->num_mixers; i++) { hw_lm = sde_crtc->mixers[i].hw_lm; hw_dspp = sde_crtc->mixers[i].hw_dspp; if (!hw_lm->cfg.right_mixer) break; } if (!hw_dspp) { DRM_ERROR("invalid dspp\n"); return; } irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE, hw_dspp->idx); if (irq_idx < 0) { DRM_ERROR("failed to get irq idx\n"); return; } spin_lock_irqsave(&sde_crtc->spin_lock, flags); node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, sde_crtc); spin_unlock_irqrestore(&sde_crtc->spin_lock, flags); if (!node) return; if (node->state == IRQ_DISABLED) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) DRM_ERROR("failed to enable irq %d\n", irq_idx); else node->state = IRQ_ENABLED; } } static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, struct sde_crtc *sde_crtc) { Loading Loading @@ -640,6 +723,21 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, } hw_lm->ops.setup_gc(hw_lm, &hw_cfg); break; case SDE_CP_CRTC_DSPP_HIST_CTRL: if (!hw_dspp || !hw_dspp->ops.setup_histogram) { ret = -EINVAL; continue; } hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled); break; case SDE_CP_CRTC_DSPP_HIST_IRQ: if (!hw_dspp || !hw_lm) { ret = -EINVAL; continue; } if (!hw_lm->cfg.right_mixer) _sde_cp_crtc_enable_hist_irq(sde_crtc); break; case SDE_CP_CRTC_DSPP_AD_MODE: if (!hw_dspp || !hw_dspp->ops.setup_ad) { ret = -EINVAL; Loading Loading @@ -1034,6 +1132,9 @@ void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc) kfree(prop_node); } if (sde_crtc->hist_blob) drm_property_unreference_blob(sde_crtc->hist_blob); mutex_destroy(&sde_crtc->crtc_cp_lock); INIT_LIST_HEAD(&sde_crtc->active_list); INIT_LIST_HEAD(&sde_crtc->dirty_list); Loading Loading @@ -1295,6 +1396,30 @@ static void dspp_igc_install_property(struct drm_crtc *crtc) } } static void dspp_hist_install_property(struct drm_crtc *crtc) { struct sde_kms *kms = NULL; struct sde_mdss_cfg *catalog = NULL; u32 version; kms = get_kms(crtc); catalog = kms->catalog; version = catalog->dspp[0].sblk->hist.version >> 16; switch (version) { case 1: sde_cp_crtc_install_enum_property(crtc, SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes, ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1"); sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1", SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0); break; default: DRM_ERROR("version %d not supported\n", version); break; } } static void sde_cp_update_list(struct sde_cp_node *prop_node, struct sde_crtc *crtc, bool dirty_list) { Loading Loading @@ -1416,6 +1541,7 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, int i; int irq_idx, ret; struct sde_cp_node prop_node; struct sde_crtc_irq_info *node = NULL; if (!crtc_drm || !ad_irq) { DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, ad_irq); Loading Loading @@ -1460,8 +1586,23 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, goto exit; } node = _sde_cp_get_intr_node(DRM_EVENT_AD_BACKLIGHT, crtc); if (!en) { sde_core_irq_disable(kms, &irq_idx, 1); if (node) { if (node->state == IRQ_ENABLED) { ret = sde_core_irq_disable(kms, &irq_idx, 1); if (ret) DRM_ERROR("disable irq %d error %d\n", irq_idx, ret); else node->state = IRQ_NOINIT; } else { node->state = IRQ_NOINIT; } } else { DRM_ERROR("failed to get node from crtc event list\n"); } sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); ret = 0; goto exit; Loading @@ -1474,10 +1615,30 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, DRM_ERROR("failed to register the callback ret %d\n", ret); goto exit; } if (node) { /* device resume or resume from IPC cases */ if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("enable irq %d error %d\n", irq_idx, ret); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); } else { node->state = IRQ_ENABLED; } } } else { /* request from userspace to register the event * in this case, node has not been added into the event list */ ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("failed to enable irq ret %d\n", ret); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); } } exit: return ret; Loading Loading @@ -1540,3 +1701,201 @@ void sde_cp_crtc_post_ipc(struct drm_crtc *drm_crtc) sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESUME); } static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx) { struct sde_crtc *crtc = arg; struct drm_crtc *crtc_drm = &crtc->base; struct sde_hw_dspp *hw_dspp; struct sde_kms *kms; struct sde_crtc_irq_info *node = NULL; u32 i; int ret = 0; unsigned long flags; /* disable histogram irq */ kms = get_kms(crtc_drm); spin_lock_irqsave(&crtc->spin_lock, flags); node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc); spin_unlock_irqrestore(&crtc->spin_lock, flags); if (!node) { DRM_ERROR("cannot find histogram event node in crtc\n"); return; } if (node->state == IRQ_ENABLED) { if (sde_core_irq_disable_nolock(kms, irq_idx)) { DRM_ERROR("failed to disable irq %d, ret %d\n", irq_idx, ret); return; } node->state = IRQ_DISABLED; } /* lock histogram buffer */ for (i = 0; i < crtc->num_mixers; i++) { hw_dspp = crtc->mixers[i].hw_dspp; if (hw_dspp && hw_dspp->ops.lock_histogram) hw_dspp->ops.lock_histogram(hw_dspp, NULL); } /* notify histogram event */ sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL); } static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg) { struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc *crtc; struct drm_event event; struct drm_msm_hist *hist_data; struct drm_msm_hist tmp_hist_data; u32 i, j; if (!crtc_drm) { DRM_ERROR("invalid crtc %pK\n", crtc_drm); return; } crtc = to_sde_crtc(crtc_drm); if (!crtc) { DRM_ERROR("invalid sde_crtc %pK\n", crtc); return; } if (!crtc->hist_blob) return; /* read histogram data into blob */ hist_data = (struct drm_msm_hist *)crtc->hist_blob->data; for (i = 0; i < crtc->num_mixers; i++) { hw_dspp = crtc->mixers[i].hw_dspp; if (!hw_dspp || !hw_dspp->ops.read_histogram) { DRM_ERROR("invalid dspp %pK or read_histogram func\n", hw_dspp); return; } if (!i) { hw_dspp->ops.read_histogram(hw_dspp, hist_data); } else { /* Merge hist data for DSPP0 and DSPP1 */ hw_dspp->ops.read_histogram(hw_dspp, &tmp_hist_data); for (j = 0; j < HIST_V_SIZE; j++) hist_data->data[j] += tmp_hist_data.data[j]; } } /* send histogram event with blob id */ event.length = sizeof(u32); event.type = DRM_EVENT_HISTOGRAM; msm_mode_object_event_notify(&crtc_drm->base, crtc_drm->dev, &event, (u8 *)(&crtc->hist_blob->base.id)); } int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *hist_irq) { struct sde_kms *kms = NULL; u32 num_mixers; struct sde_hw_mixer *hw_lm; struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc *crtc; struct sde_crtc_irq_info *node = NULL; int i, irq_idx, ret = 0; if (!crtc_drm || !hist_irq) { DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq); return -EINVAL; } crtc = to_sde_crtc(crtc_drm); if (!crtc) { DRM_ERROR("invalid sde_crtc %pK\n", crtc); return -EINVAL; } kms = get_kms(crtc_drm); num_mixers = crtc->num_mixers; for (i = 0; i < num_mixers; i++) { hw_lm = crtc->mixers[i].hw_lm; hw_dspp = crtc->mixers[i].hw_dspp; if (!hw_lm->cfg.right_mixer) break; } if (!hw_dspp) { DRM_ERROR("invalid dspp\n"); ret = -EINVAL; goto exit; } irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE, hw_dspp->idx); if (irq_idx < 0) { DRM_ERROR("failed to get the irq idx ret %d\n", irq_idx); ret = irq_idx; goto exit; } node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc); /* deregister histogram irq */ if (!en) { if (node) { /* device suspend case or suspend to IPC cases */ if (node->state == IRQ_ENABLED) { ret = sde_core_irq_disable(kms, &irq_idx, 1); if (ret) DRM_ERROR("disable irq %d error %d\n", irq_idx, ret); else node->state = IRQ_NOINIT; } else { node->state = IRQ_NOINIT; } } else { DRM_ERROR("failed to get node from crtc event list\n"); } sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); goto exit; } /* register histogram irq */ hist_irq->arg = crtc; hist_irq->func = sde_cp_hist_interrupt_cb; ret = sde_core_irq_register_callback(kms, irq_idx, hist_irq); if (ret) { DRM_ERROR("failed to register the callback ret %d\n", ret); goto exit; } if (node) { /* device resume or resume from IPC cases */ if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("enable irq %d error %d\n", irq_idx, ret); sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); } else { node->state = IRQ_ENABLED; } } } else { /* request from userspace to register the event * in this case, node has not been added into the event list */ ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("failed to enable irq ret %d\n", ret); sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); } } exit: return ret; } drivers/gpu/drm/msm/sde/sde_color_processing.h +28 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,25 @@ enum sde_memcolor_type { MEMCOLOR_FOLIAGE }; /* * PA HISTOGRAM modes * @HIST_DISABLED Histogram disabled * @HIST_ENABLED Histogram enabled */ enum sde_hist_modes { HIST_DISABLED, HIST_ENABLED }; /** * struct drm_prop_enum_list - drm structure for creating enum property and * enumerating values */ static const struct drm_prop_enum_list sde_hist_modes[] = { {HIST_DISABLED, "hist_off"}, {HIST_ENABLED, "hist_on"}, }; /** * sde_cp_crtc_init(): Initialize color processing lists for a crtc. * Should be called during crtc initialization. Loading Loading @@ -117,4 +136,13 @@ void sde_cp_crtc_pre_ipc(struct drm_crtc *crtc); * @crtc: Pointer to crtc. */ void sde_cp_crtc_post_ipc(struct drm_crtc *crtc); /** * sde_cp_hist_interrupt: Api to enable/disable histogram interrupt * @crtc: Pointer to crtc. * @en: Variable to enable/disable interrupt. * @irq: Pointer to irq callback */ int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *hist_irq); #endif /*_SDE_COLOR_PROCESSING_H */ drivers/gpu/drm/msm/sde/sde_crtc.c +7 −10 Original line number Diff line number Diff line Loading @@ -48,14 +48,6 @@ #define MEM_PROTECT_SD_CTRL_SWITCH 0x18 #define MDP_DEVICE_ID 0x1A struct sde_crtc_irq_info { struct sde_irq_callback irq; u32 event; int (*func)(struct drm_crtc *crtc, bool en, struct sde_irq_callback *irq); struct list_head list; }; struct sde_crtc_custom_events { u32 event; int (*func)(struct drm_crtc *crtc, bool en, Loading @@ -70,7 +62,8 @@ static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm, static struct sde_crtc_custom_events custom_events[] = { {DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt}, {DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler}, {DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler} {DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}, {DRM_EVENT_HISTOGRAM, sde_cp_hist_interrupt}, }; /* default input fence timeout, in ms */ Loading Loading @@ -5471,6 +5464,8 @@ static int _sde_crtc_event_enable(struct sde_kms *kms, if (!ret) { spin_lock_irqsave(&crtc->spin_lock, flags); /* irq is regiestered and enabled and set the state */ node->state = IRQ_ENABLED; list_add_tail(&node->list, &crtc->user_event_list); spin_unlock_irqrestore(&crtc->spin_lock, flags); } else { Loading @@ -5494,7 +5489,6 @@ static int _sde_crtc_event_disable(struct sde_kms *kms, spin_lock_irqsave(&crtc->spin_lock, flags); list_for_each_entry(node, &crtc->user_event_list, list) { if (node->event == event) { list_del(&node->list); found = true; break; } Loading @@ -5510,12 +5504,15 @@ static int _sde_crtc_event_disable(struct sde_kms *kms, * no need to disable/de-register. */ if (!crtc_drm->enabled) { list_del(&node->list); kfree(node); return 0; } priv = kms->dev->dev_private; sde_power_resource_enable(&priv->phandle, kms->core_client, true); ret = node->func(crtc_drm, false, &node->irq); list_del(&node->list); kfree(node); sde_power_resource_enable(&priv->phandle, kms->core_client, false); return ret; } Loading drivers/gpu/drm/msm/sde/sde_crtc.h +26 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,9 @@ struct sde_crtc { struct list_head rp_head; struct sde_crtc_smmu_state_data smmu_state; /* blob for histogram data */ struct drm_property_blob *hist_blob; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) Loading Loading @@ -407,6 +410,29 @@ struct sde_crtc_state { struct sde_crtc_respool rp; }; enum sde_crtc_irq_state { IRQ_NOINIT, IRQ_ENABLED, IRQ_DISABLED, }; /** * sde_crtc_irq_info - crtc interrupt info * @irq: interrupt callback * @event: event type of the interrupt * @func: function pointer to enable/disable the interrupt * @list: list of user customized event in crtc * @ref_count: reference count for the interrupt */ struct sde_crtc_irq_info { struct sde_irq_callback irq; u32 event; int (*func)(struct drm_crtc *crtc, bool en, struct sde_irq_callback *irq); struct list_head list; enum sde_crtc_irq_state state; }; #define to_sde_crtc_state(x) \ container_of(x, struct sde_crtc_state, base) Loading Loading
arch/arm64/boot/dts/qcom/sdm845-sde.dtsi +1 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ qcom,sde-dspp-gamut = <0x1000 0x00040000>; qcom,sde-dspp-pcc = <0x1700 0x00040000>; qcom,sde-dspp-gc = <0x17c0 0x00010008>; qcom,sde-dspp-hist = <0x800 0x00010007>; }; qcom,platform-supply-entries { Loading
drivers/gpu/drm/msm/sde/sde_color_processing.c +365 −6 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ static void dspp_gc_install_property(struct drm_crtc *crtc); static void dspp_igc_install_property(struct drm_crtc *crtc); static void dspp_hist_install_property(struct drm_crtc *crtc); typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc); static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX]; Loading @@ -77,6 +79,8 @@ static void sde_cp_notify_ad_event(struct drm_crtc *crtc_drm, void *arg); static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc, enum ad_property ad_prop); static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg); #define setup_dspp_prop_install_funcs(func) \ do { \ func[SDE_DSPP_PCC] = dspp_pcc_install_property; \ Loading @@ -86,6 +90,7 @@ do { \ func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \ func[SDE_DSPP_GC] = dspp_gc_install_property; \ func[SDE_DSPP_IGC] = dspp_igc_install_property; \ func[SDE_DSPP_HIST] = dspp_hist_install_property; \ } while (0) typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc); Loading @@ -111,7 +116,8 @@ enum { SDE_CP_CRTC_DSPP_SIXZONE, SDE_CP_CRTC_DSPP_GAMUT, SDE_CP_CRTC_DSPP_DITHER, SDE_CP_CRTC_DSPP_HIST, SDE_CP_CRTC_DSPP_HIST_CTRL, SDE_CP_CRTC_DSPP_HIST_IRQ, SDE_CP_CRTC_DSPP_AD, SDE_CP_CRTC_DSPP_VLUT, SDE_CP_CRTC_DSPP_AD_MODE, Loading Loading @@ -365,6 +371,12 @@ void sde_cp_crtc_init(struct drm_crtc *crtc) return; } /* create blob to store histogram data */ sde_crtc->hist_blob = drm_property_create_blob(crtc->dev, sizeof(struct drm_msm_hist), NULL); if (IS_ERR(sde_crtc->hist_blob)) sde_crtc->hist_blob = NULL; mutex_init(&sde_crtc->crtc_cp_lock); INIT_LIST_HEAD(&sde_crtc->active_list); INIT_LIST_HEAD(&sde_crtc->dirty_list); Loading Loading @@ -532,6 +544,77 @@ static void sde_cp_crtc_install_enum_property(struct drm_crtc *crtc, sde_cp_crtc_prop_attach(&prop_attach); } static struct sde_crtc_irq_info *_sde_cp_get_intr_node(u32 event, struct sde_crtc *sde_crtc) { bool found = false; struct sde_crtc_irq_info *node = NULL; list_for_each_entry(node, &sde_crtc->user_event_list, list) { if (node->event == event) { found = true; break; } } if (!found) node = NULL; return node; } static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc) { struct drm_crtc *crtc_drm = &sde_crtc->base; struct sde_kms *kms = NULL; struct sde_hw_mixer *hw_lm; struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc_irq_info *node = NULL; int i, irq_idx, ret = 0; unsigned long flags; if (!crtc_drm) { DRM_ERROR("invalid crtc %pK\n", crtc_drm); return; } kms = get_kms(crtc_drm); for (i = 0; i < sde_crtc->num_mixers; i++) { hw_lm = sde_crtc->mixers[i].hw_lm; hw_dspp = sde_crtc->mixers[i].hw_dspp; if (!hw_lm->cfg.right_mixer) break; } if (!hw_dspp) { DRM_ERROR("invalid dspp\n"); return; } irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE, hw_dspp->idx); if (irq_idx < 0) { DRM_ERROR("failed to get irq idx\n"); return; } spin_lock_irqsave(&sde_crtc->spin_lock, flags); node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, sde_crtc); spin_unlock_irqrestore(&sde_crtc->spin_lock, flags); if (!node) return; if (node->state == IRQ_DISABLED) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) DRM_ERROR("failed to enable irq %d\n", irq_idx); else node->state = IRQ_ENABLED; } } static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, struct sde_crtc *sde_crtc) { Loading Loading @@ -640,6 +723,21 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, } hw_lm->ops.setup_gc(hw_lm, &hw_cfg); break; case SDE_CP_CRTC_DSPP_HIST_CTRL: if (!hw_dspp || !hw_dspp->ops.setup_histogram) { ret = -EINVAL; continue; } hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled); break; case SDE_CP_CRTC_DSPP_HIST_IRQ: if (!hw_dspp || !hw_lm) { ret = -EINVAL; continue; } if (!hw_lm->cfg.right_mixer) _sde_cp_crtc_enable_hist_irq(sde_crtc); break; case SDE_CP_CRTC_DSPP_AD_MODE: if (!hw_dspp || !hw_dspp->ops.setup_ad) { ret = -EINVAL; Loading Loading @@ -1034,6 +1132,9 @@ void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc) kfree(prop_node); } if (sde_crtc->hist_blob) drm_property_unreference_blob(sde_crtc->hist_blob); mutex_destroy(&sde_crtc->crtc_cp_lock); INIT_LIST_HEAD(&sde_crtc->active_list); INIT_LIST_HEAD(&sde_crtc->dirty_list); Loading Loading @@ -1295,6 +1396,30 @@ static void dspp_igc_install_property(struct drm_crtc *crtc) } } static void dspp_hist_install_property(struct drm_crtc *crtc) { struct sde_kms *kms = NULL; struct sde_mdss_cfg *catalog = NULL; u32 version; kms = get_kms(crtc); catalog = kms->catalog; version = catalog->dspp[0].sblk->hist.version >> 16; switch (version) { case 1: sde_cp_crtc_install_enum_property(crtc, SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes, ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1"); sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1", SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0); break; default: DRM_ERROR("version %d not supported\n", version); break; } } static void sde_cp_update_list(struct sde_cp_node *prop_node, struct sde_crtc *crtc, bool dirty_list) { Loading Loading @@ -1416,6 +1541,7 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, int i; int irq_idx, ret; struct sde_cp_node prop_node; struct sde_crtc_irq_info *node = NULL; if (!crtc_drm || !ad_irq) { DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, ad_irq); Loading Loading @@ -1460,8 +1586,23 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, goto exit; } node = _sde_cp_get_intr_node(DRM_EVENT_AD_BACKLIGHT, crtc); if (!en) { sde_core_irq_disable(kms, &irq_idx, 1); if (node) { if (node->state == IRQ_ENABLED) { ret = sde_core_irq_disable(kms, &irq_idx, 1); if (ret) DRM_ERROR("disable irq %d error %d\n", irq_idx, ret); else node->state = IRQ_NOINIT; } else { node->state = IRQ_NOINIT; } } else { DRM_ERROR("failed to get node from crtc event list\n"); } sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); ret = 0; goto exit; Loading @@ -1474,10 +1615,30 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en, DRM_ERROR("failed to register the callback ret %d\n", ret); goto exit; } if (node) { /* device resume or resume from IPC cases */ if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("enable irq %d error %d\n", irq_idx, ret); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); } else { node->state = IRQ_ENABLED; } } } else { /* request from userspace to register the event * in this case, node has not been added into the event list */ ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("failed to enable irq ret %d\n", ret); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); sde_core_irq_unregister_callback(kms, irq_idx, ad_irq); } } exit: return ret; Loading Loading @@ -1540,3 +1701,201 @@ void sde_cp_crtc_post_ipc(struct drm_crtc *drm_crtc) sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESUME); } static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx) { struct sde_crtc *crtc = arg; struct drm_crtc *crtc_drm = &crtc->base; struct sde_hw_dspp *hw_dspp; struct sde_kms *kms; struct sde_crtc_irq_info *node = NULL; u32 i; int ret = 0; unsigned long flags; /* disable histogram irq */ kms = get_kms(crtc_drm); spin_lock_irqsave(&crtc->spin_lock, flags); node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc); spin_unlock_irqrestore(&crtc->spin_lock, flags); if (!node) { DRM_ERROR("cannot find histogram event node in crtc\n"); return; } if (node->state == IRQ_ENABLED) { if (sde_core_irq_disable_nolock(kms, irq_idx)) { DRM_ERROR("failed to disable irq %d, ret %d\n", irq_idx, ret); return; } node->state = IRQ_DISABLED; } /* lock histogram buffer */ for (i = 0; i < crtc->num_mixers; i++) { hw_dspp = crtc->mixers[i].hw_dspp; if (hw_dspp && hw_dspp->ops.lock_histogram) hw_dspp->ops.lock_histogram(hw_dspp, NULL); } /* notify histogram event */ sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL); } static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg) { struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc *crtc; struct drm_event event; struct drm_msm_hist *hist_data; struct drm_msm_hist tmp_hist_data; u32 i, j; if (!crtc_drm) { DRM_ERROR("invalid crtc %pK\n", crtc_drm); return; } crtc = to_sde_crtc(crtc_drm); if (!crtc) { DRM_ERROR("invalid sde_crtc %pK\n", crtc); return; } if (!crtc->hist_blob) return; /* read histogram data into blob */ hist_data = (struct drm_msm_hist *)crtc->hist_blob->data; for (i = 0; i < crtc->num_mixers; i++) { hw_dspp = crtc->mixers[i].hw_dspp; if (!hw_dspp || !hw_dspp->ops.read_histogram) { DRM_ERROR("invalid dspp %pK or read_histogram func\n", hw_dspp); return; } if (!i) { hw_dspp->ops.read_histogram(hw_dspp, hist_data); } else { /* Merge hist data for DSPP0 and DSPP1 */ hw_dspp->ops.read_histogram(hw_dspp, &tmp_hist_data); for (j = 0; j < HIST_V_SIZE; j++) hist_data->data[j] += tmp_hist_data.data[j]; } } /* send histogram event with blob id */ event.length = sizeof(u32); event.type = DRM_EVENT_HISTOGRAM; msm_mode_object_event_notify(&crtc_drm->base, crtc_drm->dev, &event, (u8 *)(&crtc->hist_blob->base.id)); } int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *hist_irq) { struct sde_kms *kms = NULL; u32 num_mixers; struct sde_hw_mixer *hw_lm; struct sde_hw_dspp *hw_dspp = NULL; struct sde_crtc *crtc; struct sde_crtc_irq_info *node = NULL; int i, irq_idx, ret = 0; if (!crtc_drm || !hist_irq) { DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq); return -EINVAL; } crtc = to_sde_crtc(crtc_drm); if (!crtc) { DRM_ERROR("invalid sde_crtc %pK\n", crtc); return -EINVAL; } kms = get_kms(crtc_drm); num_mixers = crtc->num_mixers; for (i = 0; i < num_mixers; i++) { hw_lm = crtc->mixers[i].hw_lm; hw_dspp = crtc->mixers[i].hw_dspp; if (!hw_lm->cfg.right_mixer) break; } if (!hw_dspp) { DRM_ERROR("invalid dspp\n"); ret = -EINVAL; goto exit; } irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE, hw_dspp->idx); if (irq_idx < 0) { DRM_ERROR("failed to get the irq idx ret %d\n", irq_idx); ret = irq_idx; goto exit; } node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc); /* deregister histogram irq */ if (!en) { if (node) { /* device suspend case or suspend to IPC cases */ if (node->state == IRQ_ENABLED) { ret = sde_core_irq_disable(kms, &irq_idx, 1); if (ret) DRM_ERROR("disable irq %d error %d\n", irq_idx, ret); else node->state = IRQ_NOINIT; } else { node->state = IRQ_NOINIT; } } else { DRM_ERROR("failed to get node from crtc event list\n"); } sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); goto exit; } /* register histogram irq */ hist_irq->arg = crtc; hist_irq->func = sde_cp_hist_interrupt_cb; ret = sde_core_irq_register_callback(kms, irq_idx, hist_irq); if (ret) { DRM_ERROR("failed to register the callback ret %d\n", ret); goto exit; } if (node) { /* device resume or resume from IPC cases */ if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) { ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("enable irq %d error %d\n", irq_idx, ret); sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); } else { node->state = IRQ_ENABLED; } } } else { /* request from userspace to register the event * in this case, node has not been added into the event list */ ret = sde_core_irq_enable(kms, &irq_idx, 1); if (ret) { DRM_ERROR("failed to enable irq ret %d\n", ret); sde_core_irq_unregister_callback(kms, irq_idx, hist_irq); } } exit: return ret; }
drivers/gpu/drm/msm/sde/sde_color_processing.h +28 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,25 @@ enum sde_memcolor_type { MEMCOLOR_FOLIAGE }; /* * PA HISTOGRAM modes * @HIST_DISABLED Histogram disabled * @HIST_ENABLED Histogram enabled */ enum sde_hist_modes { HIST_DISABLED, HIST_ENABLED }; /** * struct drm_prop_enum_list - drm structure for creating enum property and * enumerating values */ static const struct drm_prop_enum_list sde_hist_modes[] = { {HIST_DISABLED, "hist_off"}, {HIST_ENABLED, "hist_on"}, }; /** * sde_cp_crtc_init(): Initialize color processing lists for a crtc. * Should be called during crtc initialization. Loading Loading @@ -117,4 +136,13 @@ void sde_cp_crtc_pre_ipc(struct drm_crtc *crtc); * @crtc: Pointer to crtc. */ void sde_cp_crtc_post_ipc(struct drm_crtc *crtc); /** * sde_cp_hist_interrupt: Api to enable/disable histogram interrupt * @crtc: Pointer to crtc. * @en: Variable to enable/disable interrupt. * @irq: Pointer to irq callback */ int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *hist_irq); #endif /*_SDE_COLOR_PROCESSING_H */
drivers/gpu/drm/msm/sde/sde_crtc.c +7 −10 Original line number Diff line number Diff line Loading @@ -48,14 +48,6 @@ #define MEM_PROTECT_SD_CTRL_SWITCH 0x18 #define MDP_DEVICE_ID 0x1A struct sde_crtc_irq_info { struct sde_irq_callback irq; u32 event; int (*func)(struct drm_crtc *crtc, bool en, struct sde_irq_callback *irq); struct list_head list; }; struct sde_crtc_custom_events { u32 event; int (*func)(struct drm_crtc *crtc, bool en, Loading @@ -70,7 +62,8 @@ static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm, static struct sde_crtc_custom_events custom_events[] = { {DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt}, {DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler}, {DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler} {DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}, {DRM_EVENT_HISTOGRAM, sde_cp_hist_interrupt}, }; /* default input fence timeout, in ms */ Loading Loading @@ -5471,6 +5464,8 @@ static int _sde_crtc_event_enable(struct sde_kms *kms, if (!ret) { spin_lock_irqsave(&crtc->spin_lock, flags); /* irq is regiestered and enabled and set the state */ node->state = IRQ_ENABLED; list_add_tail(&node->list, &crtc->user_event_list); spin_unlock_irqrestore(&crtc->spin_lock, flags); } else { Loading @@ -5494,7 +5489,6 @@ static int _sde_crtc_event_disable(struct sde_kms *kms, spin_lock_irqsave(&crtc->spin_lock, flags); list_for_each_entry(node, &crtc->user_event_list, list) { if (node->event == event) { list_del(&node->list); found = true; break; } Loading @@ -5510,12 +5504,15 @@ static int _sde_crtc_event_disable(struct sde_kms *kms, * no need to disable/de-register. */ if (!crtc_drm->enabled) { list_del(&node->list); kfree(node); return 0; } priv = kms->dev->dev_private; sde_power_resource_enable(&priv->phandle, kms->core_client, true); ret = node->func(crtc_drm, false, &node->irq); list_del(&node->list); kfree(node); sde_power_resource_enable(&priv->phandle, kms->core_client, false); return ret; } Loading
drivers/gpu/drm/msm/sde/sde_crtc.h +26 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,9 @@ struct sde_crtc { struct list_head rp_head; struct sde_crtc_smmu_state_data smmu_state; /* blob for histogram data */ struct drm_property_blob *hist_blob; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) Loading Loading @@ -407,6 +410,29 @@ struct sde_crtc_state { struct sde_crtc_respool rp; }; enum sde_crtc_irq_state { IRQ_NOINIT, IRQ_ENABLED, IRQ_DISABLED, }; /** * sde_crtc_irq_info - crtc interrupt info * @irq: interrupt callback * @event: event type of the interrupt * @func: function pointer to enable/disable the interrupt * @list: list of user customized event in crtc * @ref_count: reference count for the interrupt */ struct sde_crtc_irq_info { struct sde_irq_callback irq; u32 event; int (*func)(struct drm_crtc *crtc, bool en, struct sde_irq_callback *irq); struct list_head list; enum sde_crtc_irq_state state; }; #define to_sde_crtc_state(x) \ container_of(x, struct sde_crtc_state, base) Loading