// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[sde_rsc:%s:%d]: " fmt, __func__, __LINE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "msm_drv.h" #include "sde_rsc_priv.h" #include "sde_dbg.h" #include "sde_trace.h" #define SDE_RSC_DRV_DBG_NAME "sde_rsc_drv" #define SDE_RSC_WRAPPER_DBG_NAME "sde_rsc_wrapper" #define SINGLE_TCS_EXECUTION_TIME_V1 1064000 #define SINGLE_TCS_EXECUTION_TIME_V2 930000 #define SINGLE_TCS_EXECUTION_TIME_V3 930000 #define SINGLE_TCS_EXECUTION_TIME_V4 930000 #define SINGLE_TCS_EXECUTION_TIME_V5 650000 #define RSC_MODE_INSTRUCTION_TIME 100 #define RSC_MODE_THRESHOLD_OVERHEAD 2700 /** * rsc_min_threshold will be set to MIN_THRESHOLD_OVERHEAD_TIME which * takes into account back off time + overhead from RSC/RSC_WRAPPER. The * overhead buffer time is required to be greater than 14. Program it * with a higher value (3.3 ms), so it has sufficient time to complete * the sequence in rare cases. */ #define MIN_THRESHOLD_OVERHEAD_TIME 64 #define DEFAULT_PANEL_FPS 60 #define DEFAULT_PANEL_JITTER_NUMERATOR 2 #define DEFAULT_PANEL_JITTER_DENOMINATOR 1 #define DEFAULT_PANEL_PREFILL_LINES 25 #define DEFAULT_PANEL_VTOTAL (480 + DEFAULT_PANEL_PREFILL_LINES) #define TICKS_IN_NANO_SECOND 1000000000 #define MAX_BUFFER_SIZE 256 #define CMD_MODE_SWITCH_SUCCESS 0xFFFF #define VID_MODE_SWITCH_SUCCESS 0xFFFE #define CLK_MODE_SWITCH_SUCCESS 0xFFFD #define STATE_UPDATE_NOT_ALLOWED 0xFFFC /* Primary panel worst case VSYNC expected to be no less than 30fps */ #define PRIMARY_VBLANK_WORST_CASE_MS 34 #define DEFAULT_PANEL_MIN_V_PREFILL 35 /* add 10ms constant for low fps cases and use default timeout for existing cases */ #define RSC_VSYNC_TIMEOUT_MS(x) ((x && x->cmd_config.fps < 30) ? \ ((1000 / x->cmd_config.fps) + 10) : PRIMARY_VBLANK_WORST_CASE_MS) static struct sde_rsc_priv *rsc_prv_list[MAX_RSC_COUNT]; static struct device *rpmh_dev[MAX_RSC_COUNT]; /** * sde_rsc_client_create() - create the client for sde rsc. * Different displays like DSI, HDMI, DP, WB, etc should call this * api to register their vote for rpmh. They still need to vote for * power handle to get the clocks. * @rsc_index: A client will be created on this RSC. As of now only * SDE_RSC_INDEX is valid rsc index. * @name: Caller needs to provide some valid string to identify * the client. "primary", "dp", "hdmi" are suggested name. * @is_primary: Caller needs to provide information if client is primary * or not. Primary client votes will be redirected to * display rsc. * @vsync_source: This parameter is only valid for primary display. It provides * vsync source information * * Return: client node pointer. */ struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index, char *client_name, enum sde_rsc_client_type client_type, u32 vsync_source) { struct sde_rsc_client *client; struct sde_rsc_priv *rsc; static int id; if (!client_name) { pr_err("client name is null- not supported\n"); return ERR_PTR(-EINVAL); } else if (rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return ERR_PTR(-EINVAL); } else if (!rsc_prv_list[rsc_index]) { pr_debug("rsc not probed yet or not available\n"); return NULL; } rsc = rsc_prv_list[rsc_index]; client = kzalloc(sizeof(struct sde_rsc_client), GFP_KERNEL); if (!client) return ERR_PTR(-ENOMEM); mutex_lock(&rsc->client_lock); strlcpy(client->name, client_name, MAX_RSC_CLIENT_NAME_LEN); client->current_state = SDE_RSC_IDLE_STATE; client->rsc_index = rsc_index; client->id = id; client->client_type = client_type; if (client->client_type == SDE_RSC_PRIMARY_DISP_CLIENT) { rsc->primary_client = client; rsc->vsync_source = vsync_source; } pr_debug("client %s rsc index:%d client_type:%d\n", client_name, rsc_index, client->client_type); list_add(&client->list, &rsc->client_list); id++; mutex_unlock(&rsc->client_lock); return client; } EXPORT_SYMBOL(sde_rsc_client_create); /** * sde_rsc_client_destroy() - Destroy the sde rsc client. * * @client: Client pointer provided by sde_rsc_client_create(). * * Return: none */ void sde_rsc_client_destroy(struct sde_rsc_client *client) { struct sde_rsc_priv *rsc; enum sde_rsc_state state; if (!client) { pr_debug("invalid client\n"); goto end; } else if (client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); goto end; } pr_debug("client %s destroyed\n", client->name); rsc = rsc_prv_list[client->rsc_index]; if (!rsc) goto end; mutex_lock(&rsc->client_lock); state = client->current_state; mutex_unlock(&rsc->client_lock); if (state != SDE_RSC_IDLE_STATE) { int wait_vblank_crtc_id; sde_rsc_client_state_update(client, SDE_RSC_IDLE_STATE, NULL, SDE_RSC_INVALID_CRTC_ID, &wait_vblank_crtc_id); /* if vblank wait required at shutdown, use a simple sleep */ if (wait_vblank_crtc_id != SDE_RSC_INVALID_CRTC_ID) { pr_err("unexpected sleep required on crtc %d at rsc client destroy\n", wait_vblank_crtc_id); SDE_EVT32(client->id, state, rsc->current_state, client->crtc_id, wait_vblank_crtc_id, SDE_EVTLOG_ERROR); msleep(RSC_VSYNC_TIMEOUT_MS(rsc)); } } mutex_lock(&rsc->client_lock); list_del_init(&client->list); mutex_unlock(&rsc->client_lock); kfree(client); end: return; } EXPORT_SYMBOL(sde_rsc_client_destroy); struct sde_rsc_event *sde_rsc_register_event(int rsc_index, uint32_t event_type, void (*cb_func)(uint32_t event_type, void *usr), void *usr) { struct sde_rsc_event *evt; struct sde_rsc_priv *rsc; if (rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index:%d\n", rsc_index); return ERR_PTR(-EINVAL); } else if (!rsc_prv_list[rsc_index]) { pr_err("rsc idx:%d not probed yet or not available\n", rsc_index); return ERR_PTR(-EINVAL); } else if (!cb_func || !event_type) { pr_err("no event or cb func\n"); return ERR_PTR(-EINVAL); } rsc = rsc_prv_list[rsc_index]; evt = kzalloc(sizeof(struct sde_rsc_event), GFP_KERNEL); if (!evt) return ERR_PTR(-ENOMEM); evt->event_type = event_type; evt->rsc_index = rsc_index; evt->usr = usr; evt->cb_func = cb_func; pr_debug("event register type:%d rsc index:%d\n", event_type, rsc_index); mutex_lock(&rsc->client_lock); list_add(&evt->list, &rsc->event_list); mutex_unlock(&rsc->client_lock); return evt; } EXPORT_SYMBOL(sde_rsc_register_event); void sde_rsc_unregister_event(struct sde_rsc_event *event) { struct sde_rsc_priv *rsc; if (!event) { pr_debug("invalid event client\n"); goto end; } else if (event->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); goto end; } pr_debug("event client destroyed\n"); rsc = rsc_prv_list[event->rsc_index]; if (!rsc) goto end; mutex_lock(&rsc->client_lock); list_del_init(&event->list); mutex_unlock(&rsc->client_lock); kfree(event); end: return; } EXPORT_SYMBOL(sde_rsc_unregister_event); bool is_sde_rsc_available(int rsc_index) { if (rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index:%d\n", rsc_index); return false; } else if (!rsc_prv_list[rsc_index]) { pr_debug("rsc idx:%d not probed yet or not available\n", rsc_index); return false; } return true; } EXPORT_SYMBOL(is_sde_rsc_available); enum sde_rsc_state get_sde_rsc_current_state(int rsc_index) { struct sde_rsc_priv *rsc; if (rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index:%d\n", rsc_index); return SDE_RSC_IDLE_STATE; } else if (!rsc_prv_list[rsc_index]) { pr_err("rsc idx:%d not probed yet or not available\n", rsc_index); return SDE_RSC_IDLE_STATE; } rsc = rsc_prv_list[rsc_index]; return rsc->current_state; } EXPORT_SYMBOL(get_sde_rsc_current_state); static u32 sde_rsc_timer_calculate(struct sde_rsc_priv *rsc, struct sde_rsc_cmd_config *cmd_config, enum sde_rsc_state state) { const u32 cxo_period_ns = 52; u64 rsc_backoff_time_ns = rsc->backoff_time_ns; u64 rsc_mode_threshold_time_ns = rsc->mode_threshold_time_ns; u64 rsc_time_slot_0_ns = rsc->time_slot_0_ns; u64 rsc_time_slot_1_ns; const u64 pdc_jitter = 20; /* 20% more */ u64 frame_time_ns, frame_jitter; u64 line_time_ns, prefill_time_ns; u64 pdc_backoff_time_ns; s64 total; int ret = 0; u32 default_prefill_lines; if (cmd_config) memcpy(&rsc->cmd_config, cmd_config, sizeof(*cmd_config)); /* calculate for 640x480 60 fps resolution by default */ if (!rsc->cmd_config.fps) rsc->cmd_config.fps = DEFAULT_PANEL_FPS; if (!rsc->cmd_config.jitter_numer) rsc->cmd_config.jitter_numer = DEFAULT_PANEL_JITTER_NUMERATOR; if (!rsc->cmd_config.jitter_denom) rsc->cmd_config.jitter_denom = DEFAULT_PANEL_JITTER_DENOMINATOR; if (!rsc->cmd_config.vtotal) rsc->cmd_config.vtotal = DEFAULT_PANEL_VTOTAL; default_prefill_lines = (rsc->cmd_config.fps * DEFAULT_PANEL_MIN_V_PREFILL) / DEFAULT_PANEL_FPS; if (!rsc->cmd_config.prefill_lines) rsc->cmd_config.prefill_lines = default_prefill_lines; pr_debug("frame fps:%d jitter_numer:%d jitter_denom:%d vtotal:%d prefill lines:%d\n", rsc->cmd_config.fps, rsc->cmd_config.jitter_numer, rsc->cmd_config.jitter_denom, rsc->cmd_config.vtotal, rsc->cmd_config.prefill_lines); /* 1 nano second */ frame_time_ns = TICKS_IN_NANO_SECOND; frame_time_ns = div_u64(frame_time_ns, rsc->cmd_config.fps); frame_jitter = frame_time_ns * rsc->cmd_config.jitter_numer; frame_jitter = div_u64(frame_jitter, rsc->cmd_config.jitter_denom); /* convert it to percentage */ frame_jitter = div_u64(frame_jitter, 100); line_time_ns = frame_time_ns; line_time_ns = div_u64(line_time_ns, rsc->cmd_config.vtotal); prefill_time_ns = line_time_ns * rsc->cmd_config.prefill_lines; total = frame_time_ns - frame_jitter - prefill_time_ns; if (total < 0) { pr_err("invalid total time period time:%llu jiter_time:%llu blanking time:%llu\n", frame_time_ns, frame_jitter, prefill_time_ns); total = 0; } total = div_u64(total, cxo_period_ns); rsc->timer_config.static_wakeup_time_ns = total; pr_debug("frame time:%llu frame jiter_time:%llu\n", frame_time_ns, frame_jitter); pr_debug("line time:%llu prefill time ps:%llu\n", line_time_ns, prefill_time_ns); pr_debug("static wakeup time:%lld cxo:%u\n", total, cxo_period_ns); pdc_backoff_time_ns = rsc_backoff_time_ns; rsc_backoff_time_ns = div_u64(rsc_backoff_time_ns, cxo_period_ns); rsc->timer_config.rsc_backoff_time_ns = (u32) rsc_backoff_time_ns; pdc_backoff_time_ns *= pdc_jitter; pdc_backoff_time_ns = div_u64(pdc_backoff_time_ns, 100); rsc->timer_config.pdc_backoff_time_ns = (u32) pdc_backoff_time_ns; rsc_mode_threshold_time_ns = div_u64(rsc_mode_threshold_time_ns, cxo_period_ns); rsc->timer_config.rsc_mode_threshold_time_ns = (u32) rsc_mode_threshold_time_ns; /* time_slot_0 for mode0 latency */ rsc_time_slot_0_ns = div_u64(rsc_time_slot_0_ns, cxo_period_ns); rsc->timer_config.rsc_time_slot_0_ns = (u32) rsc_time_slot_0_ns; /* time_slot_1 for mode1 latency - 1 fps */ rsc_time_slot_1_ns = div_u64(TICKS_IN_NANO_SECOND, cxo_period_ns); rsc->timer_config.rsc_time_slot_1_ns = (u32) rsc_time_slot_1_ns; /* mode 2 is infinite */ rsc->timer_config.rsc_time_slot_2_ns = 0xFFFFFFFF; rsc->timer_config.min_threshold_time_ns = MIN_THRESHOLD_OVERHEAD_TIME; rsc->timer_config.bwi_threshold_time_ns = rsc->timer_config.rsc_time_slot_0_ns; /* timer update should be called with client call */ if (cmd_config && rsc->hw_ops.timer_update) { ret = rsc->hw_ops.timer_update(rsc); if (ret) pr_err("sde rsc: hw timer update failed ret:%d\n", ret); /* rsc init should be called during rsc probe - one time only */ } else if (rsc->hw_ops.init) { ret = rsc->hw_ops.init(rsc); if (ret) pr_err("sde rsc: hw init failed ret:%d\n", ret); } return ret; } static int sde_rsc_resource_disable(struct sde_rsc_priv *rsc) { struct sde_power_handle *phandle; struct dss_module_power *mp; if (!rsc) { pr_err("invalid drv data\n"); return -EINVAL; } if (atomic_read(&rsc->resource_refcount) == 0) { pr_err("%pS: invalid rsc resource disable call\n", __builtin_return_address(0)); return -EINVAL; } if (atomic_dec_return(&rsc->resource_refcount) != 0) return 0; phandle = &rsc->phandle; mp = &phandle->mp; msm_dss_enable_clk(mp->clk_config, mp->num_clk, false); sde_power_scale_reg_bus(phandle, VOTE_INDEX_DISABLE, false); msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); return 0; } static int sde_rsc_resource_enable(struct sde_rsc_priv *rsc) { struct sde_power_handle *phandle; struct dss_module_power *mp; int rc = 0; if (!rsc) { pr_err("invalid drv data\n"); return -EINVAL; } if (atomic_inc_return(&rsc->resource_refcount) != 1) return 0; phandle = &rsc->phandle; mp = &phandle->mp; rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, true); if (rc) { pr_err("failed to enable vregs rc=%d\n", rc); goto end; } rc = sde_power_scale_reg_bus(phandle, VOTE_INDEX_LOW, false); if (rc) { pr_err("failed to set reg bus vote rc=%d\n", rc); goto reg_bus_hdl_err; } rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true); if (rc) { pr_err("clock enable failed rc:%d\n", rc); goto clk_err; } return rc; clk_err: sde_power_scale_reg_bus(phandle, VOTE_INDEX_DISABLE, false); reg_bus_hdl_err: msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false); end: atomic_dec(&rsc->resource_refcount); return rc; } static int sde_rsc_switch_to_cmd(struct sde_rsc_priv *rsc, struct sde_rsc_cmd_config *config, struct sde_rsc_client *caller_client, int *wait_vblank_crtc_id) { struct sde_rsc_client *client; int rc = STATE_UPDATE_NOT_ALLOWED; if (!rsc->primary_client) { pr_err("primary client not available for cmd state switch\n"); rc = -EINVAL; goto end; } else if (caller_client != rsc->primary_client) { pr_err("primary client state:%d not cmd state request\n", rsc->primary_client->current_state); rc = -EINVAL; goto end; } /* update timers - might not be available at next switch */ if (config) sde_rsc_timer_calculate(rsc, config, SDE_RSC_CMD_STATE); /** * rsc clients can still send config at any time. If a config is * received during cmd_state then vsync_wait will execute with the logic * below. If a config is received when rsc is in AMC mode; A mode * switch will do the vsync wait. updated checks still support all cases * for dynamic mode switch and inline rotation. */ if (rsc->current_state == SDE_RSC_CMD_STATE) { rc = 0; if (config && rsc->version < SDE_RSC_REV_3) goto vsync_wait; else goto end; } /* any non-primary clk state client blocks the cmd state switch */ list_for_each_entry(client, &rsc->client_list, list) if (client->current_state == SDE_RSC_CLK_STATE && client->client_type == SDE_RSC_EXTERNAL_DISP_CLIENT) goto end; if (rsc->hw_ops.state_update) { rc = rsc->hw_ops.state_update(rsc, SDE_RSC_CMD_STATE); if (!rc) rpmh_mode_solver_set(rsc->rpmh_dev, true); } /* vsync wait not needed during VID->CMD switch (rev 4+ HW only) */ if (rsc->current_state == SDE_RSC_VID_STATE && rsc->version >= SDE_RSC_REV_4) { rc = 0; goto end; } vsync_wait: /* indicate wait for vsync for vid to cmd state switch & cfg update */ if (!rc && (rsc->current_state == SDE_RSC_VID_STATE || rsc->current_state == SDE_RSC_CMD_STATE)) { rsc->post_poms = true; /* clear VSYNC timestamp for indication when update completes */ if (rsc->hw_ops.hw_vsync) rsc->hw_ops.hw_vsync(rsc, VSYNC_ENABLE, NULL, 0, 0); if (!wait_vblank_crtc_id) { pr_err("invalid crtc id wait pointer, client %d\n", caller_client->id); SDE_EVT32(caller_client->id, rsc->current_state, caller_client->crtc_id, wait_vblank_crtc_id, SDE_EVTLOG_ERROR); msleep(RSC_VSYNC_TIMEOUT_MS(rsc)); } else { *wait_vblank_crtc_id = rsc->primary_client->crtc_id; } } end: return rc; } static int sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc, int *wait_vblank_crtc_id) { struct sde_rsc_client *client; int rc = STATE_UPDATE_NOT_ALLOWED; bool multi_display_active = false; bool vid_display_active = false, cmd_display_active = false; list_for_each_entry(client, &rsc->client_list, list) { if (client->current_state == SDE_RSC_CLK_STATE && client->client_type == SDE_RSC_EXTERNAL_DISP_CLIENT) multi_display_active = true; else if (client->current_state == SDE_RSC_VID_STATE) vid_display_active = true; else if (client->current_state == SDE_RSC_CMD_STATE) cmd_display_active = true; } pr_debug("multi_display:%d vid_display:%d cmd_display:%d\n", multi_display_active, vid_display_active, cmd_display_active); if (!multi_display_active && (vid_display_active || cmd_display_active)) goto end; if (rsc->hw_ops.state_update) { rc = rsc->hw_ops.state_update(rsc, SDE_RSC_CLK_STATE); if (!rc) rpmh_mode_solver_set(rsc->rpmh_dev, false); } /* indicate wait for vsync for cmd/vid to clk state switch */ if (!rc && rsc->primary_client && (rsc->current_state == SDE_RSC_CMD_STATE || rsc->current_state == SDE_RSC_VID_STATE)) { /* clear VSYNC timestamp for indication when update completes */ if (rsc->hw_ops.hw_vsync) rsc->hw_ops.hw_vsync(rsc, VSYNC_ENABLE, NULL, 0, 0); if (!wait_vblank_crtc_id) { pr_err("invalid crtc id wait pointer provided\n"); msleep(RSC_VSYNC_TIMEOUT_MS(rsc)); } else { *wait_vblank_crtc_id = rsc->primary_client->crtc_id; /* increase refcount, so we wait for the next vsync */ atomic_inc(&rsc->rsc_vsync_wait); SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait)); } } else if (atomic_read(&rsc->rsc_vsync_wait)) { SDE_EVT32(rsc->primary_client, rsc->current_state, atomic_read(&rsc->rsc_vsync_wait)); /* Wait for the vsync, if the refcount is set */ rc = wait_event_timeout(rsc->rsc_vsync_waitq, atomic_read(&rsc->rsc_vsync_wait) == 0, msecs_to_jiffies(RSC_VSYNC_TIMEOUT_MS(rsc) * 2)); if (!rc) { pr_err("Timeout waiting for vsync\n"); rc = -ETIMEDOUT; SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait), rc, SDE_EVTLOG_ERROR); } else { SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait), rc); rc = 0; } } end: return rc; } static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc, struct sde_rsc_cmd_config *config, struct sde_rsc_client *caller_client, int *wait_vblank_crtc_id) { struct sde_rsc_client *client; int rc = STATE_UPDATE_NOT_ALLOWED; if (!rsc->primary_client) { pr_err("primary client not available for vid state switch\n"); rc = -EINVAL; goto end; } else if (caller_client != rsc->primary_client) { pr_err("primary client state:%d not vid state request\n", rsc->primary_client->current_state); rc = -EINVAL; goto end; } /* update timers - might not be available at next switch */ if (config) sde_rsc_timer_calculate(rsc, config, SDE_RSC_VID_STATE); /** * rsc clients can still send config at any time. If a config is * received during vid_state then vsync_wait will execute with the logic * below. */ if (rsc->current_state == SDE_RSC_VID_STATE) { rc = 0; if (config && rsc->version < SDE_RSC_REV_3) goto vsync_wait; else goto end; } /* any non-primary clk state client blocks the vid state switch */ list_for_each_entry(client, &rsc->client_list, list) if (client->current_state == SDE_RSC_CLK_STATE && client->client_type == SDE_RSC_EXTERNAL_DISP_CLIENT) goto end; if (rsc->hw_ops.state_update) { rc = rsc->hw_ops.state_update(rsc, SDE_RSC_VID_STATE); if (!rc) rpmh_mode_solver_set(rsc->rpmh_dev, rsc->version >= SDE_RSC_REV_3); } /* vsync wait not needed during CMD->VID switch (rev 4+ HW only) */ if (rsc->current_state == SDE_RSC_CMD_STATE && rsc->version >= SDE_RSC_REV_4) { rc = 0; goto end; } vsync_wait: /* indicate wait for vsync for vid to cmd state switch & cfg update */ if (!rc && (rsc->current_state == SDE_RSC_VID_STATE || rsc->current_state == SDE_RSC_CMD_STATE)) { rsc->post_poms = true; /* clear VSYNC timestamp for indication when update completes */ if (rsc->hw_ops.hw_vsync) rsc->hw_ops.hw_vsync(rsc, VSYNC_ENABLE, NULL, 0, 0); if (!wait_vblank_crtc_id) { pr_err("invalid crtc id wait pointer, client %d\n", caller_client->id); SDE_EVT32(caller_client->id, rsc->current_state, caller_client->crtc_id, wait_vblank_crtc_id, SDE_EVTLOG_ERROR); msleep(RSC_VSYNC_TIMEOUT_MS(rsc)); } else { *wait_vblank_crtc_id = rsc->primary_client->crtc_id; } } end: return rc; } static int sde_rsc_switch_to_idle(struct sde_rsc_priv *rsc, struct sde_rsc_cmd_config *config, struct sde_rsc_client *caller_client, int *wait_vblank_crtc_id) { struct sde_rsc_client *client; int rc = STATE_UPDATE_NOT_ALLOWED; bool clk_client_active = false, multi_display_active = false; bool vid_display_active = false, cmd_display_active = false; /* * following code needs to run the loop through each * client because they might be in different order * sorting is not possible; only preference is available */ list_for_each_entry(client, &rsc->client_list, list) { if (client->current_state == SDE_RSC_CLK_STATE && client->client_type == SDE_RSC_EXTERNAL_DISP_CLIENT) multi_display_active = true; else if (client->current_state == SDE_RSC_CLK_STATE && client->client_type == SDE_RSC_CLK_CLIENT) clk_client_active = true; else if (client->current_state == SDE_RSC_VID_STATE) vid_display_active = true; else if (client->current_state == SDE_RSC_CMD_STATE) cmd_display_active = true; pr_debug("client state:%d type:%d\n", client->current_state, client->client_type); } pr_debug("multi_display:%d clk_client:%d vid_display:%d cmd_display:%d\n", multi_display_active, clk_client_active, vid_display_active, cmd_display_active); if (vid_display_active && !multi_display_active) { rc = sde_rsc_switch_to_vid(rsc, NULL, rsc->primary_client, wait_vblank_crtc_id); if (!rc) rc = VID_MODE_SWITCH_SUCCESS; } else if (cmd_display_active && !multi_display_active) { rc = sde_rsc_switch_to_cmd(rsc, NULL, rsc->primary_client, wait_vblank_crtc_id); if (!rc) rc = CMD_MODE_SWITCH_SUCCESS; } else if (clk_client_active) { rc = sde_rsc_switch_to_clk(rsc, wait_vblank_crtc_id); if (!rc) rc = CLK_MODE_SWITCH_SUCCESS; } else if (rsc->hw_ops.state_update) { rc = rsc->hw_ops.state_update(rsc, SDE_RSC_IDLE_STATE); rsc->post_poms = false; if (!rc) rpmh_mode_solver_set(rsc->rpmh_dev, true); } return rc; } /** * sde_rsc_client_get_vsync_refcount() - returns the status of the vsync * refcount, to signal if the client needs to reset the refcounting logic * @client: Client pointer provided by sde_rsc_client_create(). * * Return: value of the vsync refcount. */ int sde_rsc_client_get_vsync_refcount( struct sde_rsc_client *caller_client) { struct sde_rsc_priv *rsc; if (!caller_client) { pr_err("invalid client for rsc state update\n"); return -EINVAL; } else if (caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return -EINVAL; } rsc = rsc_prv_list[caller_client->rsc_index]; if (!rsc) return 0; return atomic_read(&rsc->rsc_vsync_wait); } /** * sde_rsc_client_reset_vsync_refcount() - reduces the refcounting * logic that waits for the vsync. * @client: Client pointer provided by sde_rsc_client_create(). * * Return: zero if refcount was already zero. */ int sde_rsc_client_reset_vsync_refcount( struct sde_rsc_client *caller_client) { struct sde_rsc_priv *rsc; int ret; if (!caller_client) { pr_err("invalid client for rsc state update\n"); return -EINVAL; } else if (caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return -EINVAL; } rsc = rsc_prv_list[caller_client->rsc_index]; if (!rsc) return 0; ret = atomic_add_unless(&rsc->rsc_vsync_wait, -1, 0); wake_up_all(&rsc->rsc_vsync_waitq); SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait)); return ret; } /** * sde_rsc_client_is_state_update_complete() - check if state update is complete * RSC state transition is not complete until HW receives VBLANK signal. This * function checks RSC HW to determine whether that signal has been received. * @client: Client pointer provided by sde_rsc_client_create(). * * Return: true if the state update has completed. */ bool sde_rsc_client_is_state_update_complete( struct sde_rsc_client *caller_client) { struct sde_rsc_priv *rsc; u32 vsync_timestamp0 = 0; if (!caller_client) { pr_err("invalid client for rsc state update\n"); return false; } else if (caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return false; } rsc = rsc_prv_list[caller_client->rsc_index]; if (!rsc) return false; /** * state updates clear VSYNC timestamp, check if a new one arrived. * use VSYNC mode 0 (CMD TE) always for this, per HW recommendation. */ if (rsc->hw_ops.hw_vsync) vsync_timestamp0 = rsc->hw_ops.hw_vsync(rsc, VSYNC_READ_VSYNC0, NULL, 0, 0); return vsync_timestamp0 != 0; } /** * sde_rsc_client_state_update() - rsc client state update * Video mode, cmd mode and clk state are suppoed as modes. A client need to * set this property during panel config time. A switching client can set the * property to change the state * * @client: Client pointer provided by sde_rsc_client_create(). * @state: Client state - video/cmd * @config: fps, vtotal, porches, etc configuration for command mode * panel * @crtc_id: current client's crtc id * @wait_vblank_crtc_id: Output parameter. If set to non-zero, rsc hw * state update requires a wait for one vblank on * the primary crtc. In that case, this output * param will be set to the crtc on which to wait. * If SDE_RSC_INVALID_CRTC_ID, no wait necessary * * Return: error code. */ int sde_rsc_client_state_update(struct sde_rsc_client *caller_client, enum sde_rsc_state state, struct sde_rsc_cmd_config *config, int crtc_id, int *wait_vblank_crtc_id) { int rc = 0; struct sde_rsc_priv *rsc; if (!caller_client) { pr_err("invalid client for rsc state update\n"); return -EINVAL; } else if (caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return -EINVAL; } rsc = rsc_prv_list[caller_client->rsc_index]; if (!rsc) return -EINVAL; if (wait_vblank_crtc_id) *wait_vblank_crtc_id = SDE_RSC_INVALID_CRTC_ID; mutex_lock(&rsc->client_lock); SDE_EVT32_VERBOSE(caller_client->id, caller_client->current_state, state, rsc->current_state, SDE_EVTLOG_FUNC_ENTRY); pr_debug("%pS: rsc state:%d request client:%s state:%d\n", __builtin_return_address(0), rsc->current_state, caller_client->name, state); if ((state == SDE_RSC_VID_STATE) && (rsc->version >= SDE_RSC_REV_3)) state = SDE_RSC_CLK_STATE; /** * This can only happen if splash is active or qsync is enabled. * In both cases timers need to be updated for when a transition to * solver occurs. Update timers now as config might not be available * at next switch. Updates for cmd/vid are handled when switching to * those states. */ if (config && (state == SDE_RSC_CLK_STATE) && (caller_client == rsc->primary_client)) sde_rsc_timer_calculate(rsc, config, state); caller_client->crtc_id = crtc_id; caller_client->current_state = state; if ((rsc->current_state == state) && !config) { SDE_EVT32(caller_client->id, caller_client->current_state, state, rsc->current_state, SDE_EVTLOG_FUNC_CASE3); goto end; } if (rsc->current_state == SDE_RSC_IDLE_STATE) sde_rsc_resource_enable(rsc); switch (state) { case SDE_RSC_IDLE_STATE: rc = sde_rsc_switch_to_idle(rsc, NULL, rsc->primary_client, wait_vblank_crtc_id); if (rc == CMD_MODE_SWITCH_SUCCESS) { state = SDE_RSC_CMD_STATE; rc = 0; } else if (rc == VID_MODE_SWITCH_SUCCESS) { state = SDE_RSC_VID_STATE; rc = 0; } else if (rc == CLK_MODE_SWITCH_SUCCESS) { state = SDE_RSC_CLK_STATE; rc = 0; } break; case SDE_RSC_CMD_STATE: rc = sde_rsc_switch_to_cmd(rsc, config, caller_client, wait_vblank_crtc_id); break; case SDE_RSC_VID_STATE: rc = sde_rsc_switch_to_vid(rsc, config, caller_client, wait_vblank_crtc_id); break; case SDE_RSC_CLK_STATE: rc = sde_rsc_switch_to_clk(rsc, wait_vblank_crtc_id); break; default: pr_err("invalid state handling %d\n", state); break; } if (rc == STATE_UPDATE_NOT_ALLOWED) { rc = 0; SDE_EVT32(caller_client->id, caller_client->current_state, state, rsc->current_state, rc, SDE_EVTLOG_FUNC_CASE1); goto clk_disable; } else if (rc) { pr_debug("state:%d update failed rc:%d\n", state, rc); SDE_EVT32(caller_client->id, caller_client->current_state, state, rsc->current_state, rc, SDE_EVTLOG_FUNC_CASE2); goto clk_disable; } pr_debug("state switch successfully complete: %d\n", state); SDE_ATRACE_INT("rsc_state", state); SDE_EVT32(caller_client->id, caller_client->current_state, state, rsc->current_state, SDE_EVTLOG_FUNC_EXIT); rsc->current_state = state; rsc->update_tcs_content = true; clk_disable: if (rsc->current_state == SDE_RSC_IDLE_STATE) sde_rsc_resource_disable(rsc); end: mutex_unlock(&rsc->client_lock); return rc; } EXPORT_SYMBOL(sde_rsc_client_state_update); /** * sde_rsc_client_vote() - ab/ib vote from rsc client * * @client: Client pointer provided by sde_rsc_client_create(). * @bus_id: data bus for which to be voted * @ab: aggregated bandwidth vote from client. * @ib: instant bandwidth vote from client. * * Return: error code. */ int sde_rsc_client_vote(struct sde_rsc_client *caller_client, u32 bus_id, u64 ab_vote, u64 ib_vote) { int rsc_index; struct sde_rsc_priv *rsc; if (caller_client && caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc client or client index\n"); return -EINVAL; } rsc_index = caller_client ? caller_client->rsc_index : SDE_RSC_INDEX; rsc = rsc_prv_list[rsc_index]; if (!rsc || bus_id >= SDE_POWER_HANDLE_DBUS_ID_MAX) return -EINVAL; pr_debug("client:%s ab:%llu ib:%llu\n", caller_client ? caller_client->name : "unknown", ab_vote, ib_vote); mutex_lock(&rsc->client_lock); rsc->bw_config.new_ab_vote[bus_id] = ab_vote; rsc->bw_config.new_ib_vote[bus_id] = ib_vote; mutex_unlock(&rsc->client_lock); return 0; } EXPORT_SYMBOL(sde_rsc_client_vote); int sde_rsc_client_trigger_vote(struct sde_rsc_client *caller_client, bool delta_vote) { int rc = 0, rsc_index, i; struct sde_rsc_priv *rsc; if (caller_client && caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return -EINVAL; } rsc_index = caller_client ? caller_client->rsc_index : SDE_RSC_INDEX; rsc = rsc_prv_list[rsc_index]; if (!rsc) return -EINVAL; if (rsc->bwi_update == BW_NO_CHANGE && !delta_vote && rsc->version >= SDE_RSC_REV_5) return 0; pr_debug("client:%s trigger bw delta vote:%d\n", caller_client ? caller_client->name : "unknown", delta_vote); mutex_lock(&rsc->client_lock); if (!delta_vote && !rsc->update_tcs_content && (rsc->current_state == SDE_RSC_CLK_STATE)) goto end; rsc->bwi_update = BW_HIGH_TO_LOW; for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX && delta_vote; i++) { if (rsc->bw_config.new_ab_vote[i] > rsc->bw_config.ab_vote[i] || rsc->bw_config.new_ib_vote[i] > rsc->bw_config.ib_vote[i]) rsc->bwi_update = BW_LOW_TO_HIGH; rsc->bw_config.ab_vote[i] = rsc->bw_config.new_ab_vote[i]; rsc->bw_config.ib_vote[i] = rsc->bw_config.new_ib_vote[i]; } rc = sde_rsc_resource_enable(rsc); if (rc < 0) goto end; if (delta_vote) { if (rsc->hw_ops.tcs_wait) { rc = rsc->hw_ops.tcs_wait(rsc); if (rc) { pr_err("tcs is still busy; can't send command\n"); if (rsc->hw_ops.tcs_use_ok) rsc->hw_ops.tcs_use_ok(rsc); goto tcs_wait_failed; } } rpmh_invalidate(rsc->rpmh_dev); for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) sde_power_data_bus_set_quota(&rsc->phandle, i, rsc->bw_config.ab_vote[i], rsc->bw_config.ib_vote[i]); rpmh_write_sleep_and_wake(rsc->rpmh_dev); } if (rsc->version >= SDE_RSC_REV_5 && !delta_vote) rsc->bwi_update = BW_NO_CHANGE; if (rsc->hw_ops.bwi_status && (rsc->current_state == SDE_RSC_CMD_STATE || rsc->current_state == SDE_RSC_VID_STATE)) rsc->hw_ops.bwi_status(rsc); else if (rsc->hw_ops.tcs_use_ok) rsc->hw_ops.tcs_use_ok(rsc); rsc->update_tcs_content = false; tcs_wait_failed: sde_rsc_resource_disable(rsc); end: mutex_unlock(&rsc->client_lock); return rc; } EXPORT_SYMBOL(sde_rsc_client_trigger_vote); #if defined(CONFIG_DEBUG_FS) void sde_rsc_debug_dump(u32 mux_sel) { struct sde_rsc_priv *rsc; rsc = rsc_prv_list[SDE_RSC_INDEX]; if (!rsc) return; /* this must be called with rsc clocks enabled */ if (rsc->hw_ops.debug_dump) rsc->hw_ops.debug_dump(rsc, mux_sel); } static int _sde_debugfs_status_show(struct seq_file *s, void *data) { struct sde_rsc_priv *rsc; struct sde_rsc_client *client; int ret; if (!s || !s->private) return -EINVAL; rsc = s->private; mutex_lock(&rsc->client_lock); seq_printf(s, "rsc current state:%d\n", rsc->current_state); seq_printf(s, "wraper backoff time(ns):%d\n", rsc->timer_config.static_wakeup_time_ns); seq_printf(s, "rsc backoff time(ns):%d\n", rsc->timer_config.rsc_backoff_time_ns); seq_printf(s, "pdc backoff time(ns):%d\n", rsc->timer_config.pdc_backoff_time_ns); seq_printf(s, "rsc mode threshold time(ns):%d\n", rsc->timer_config.rsc_mode_threshold_time_ns); seq_printf(s, "rsc time slot 0(ns):%d\n", rsc->timer_config.rsc_time_slot_0_ns); seq_printf(s, "rsc time slot 1(ns):%d\n", rsc->timer_config.rsc_time_slot_1_ns); seq_printf(s, "frame fps:%d jitter_numer:%d jitter_denom:%d vtotal:%d prefill lines:%d\n", rsc->cmd_config.fps, rsc->cmd_config.jitter_numer, rsc->cmd_config.jitter_denom, rsc->cmd_config.vtotal, rsc->cmd_config.prefill_lines); seq_puts(s, "\n"); list_for_each_entry(client, &rsc->client_list, list) seq_printf(s, "\t client:%s state:%d\n", client->name, client->current_state); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); seq_puts(s, "hw state is not supported during idle pc\n"); goto end; } if (rsc->hw_ops.debug_show) { ret = rsc->hw_ops.debug_show(s, rsc); if (ret) pr_err("sde rsc: hw debug failed ret:%d\n", ret); } end: mutex_unlock(&rsc->client_lock); return 0; } static int _sde_debugfs_status_open(struct inode *inode, struct file *file) { return single_open(file, _sde_debugfs_status_show, inode->i_private); } static int _sde_debugfs_counters_show(struct seq_file *s, void *data) { struct sde_rsc_priv *rsc = s->private; u32 counters[NUM_RSC_PROFILING_COUNTERS]; int i, ret; if (!rsc) return -EINVAL; if (!rsc->hw_ops.get_counters) { seq_puts(s, "counters are not supported on this target\n"); return 0; } memset(counters, 0, sizeof(counters)); mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("counters are not supported during idle state\n"); seq_puts(s, "no access to counters during idle pc\n"); goto end; } ret = rsc->hw_ops.get_counters(rsc, counters); if (ret) { pr_err("sde rsc: get_counters failed ret:%d\n", ret); seq_puts(s, "failed to retrieve counts!\n"); goto end; } seq_puts(s, "rsc profiling counters:\n"); for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i) seq_printf(s, "\tmode[%d] = 0x%08x:\n", i, counters[i]); end: mutex_unlock(&rsc->client_lock); return 0; } static int _sde_debugfs_counters_open(struct inode *inode, struct file *file) { return single_open(file, _sde_debugfs_counters_show, inode->i_private); } static int _sde_debugfs_generic_noseek_open(struct inode *inode, struct file *file) { /* non-seekable */ file->private_data = inode->i_private; return nonseekable_open(inode, file); } static ssize_t _sde_debugfs_profiling_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; size_t max_size = min_t(size_t, count, MAX_BUFFER_SIZE); char buffer[MAX_BUFFER_SIZE]; int blen = 0; if (*ppos || !rsc) return 0; if (!rsc->hw_ops.setup_counters) { blen += scnprintf(&buffer[blen], max_size - blen, "counters are not supported on this target\n"); goto end; } mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("counters are not supported during idle state\n"); blen += scnprintf(&buffer[blen], max_size - blen, "no access to counters during idle pc\n"); goto unlock; } blen += scnprintf(&buffer[blen], max_size - blen, "%s\n", rsc->profiling_en ? "Y" : "N"); unlock: mutex_unlock(&rsc->client_lock); end: if (copy_to_user(buf, buffer, blen)) return -EFAULT; *ppos += blen; return blen; } static ssize_t _sde_debugfs_profiling_write(struct file *file, const char __user *p, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; bool input_valid, input_value; char *input; int rc; if (!rsc || !rsc->hw_ops.setup_counters || !count || count > MAX_COUNT_SIZE_SUPPORTED) return 0; input = kmalloc(count + 1, GFP_KERNEL); if (!input) return -ENOMEM; if (copy_from_user(input, p, count)) { kfree(input); return -EFAULT; } input[count] = '\0'; switch (input[0]) { case 'y': case 'Y': case '1': input_valid = true; input_value = true; break; case 'n': case 'N': case '0': input_valid = true; input_value = false; break; default: input_valid = false; count = 0; break; } mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); count = 0; goto end; } pr_debug("input %s, profiling_en: %d\n", input_valid ? "valid" : "invalid", rsc->profiling_en); if (input_valid) { rsc->profiling_en = input_value; rc = rsc->hw_ops.setup_counters(rsc, rsc->profiling_en); if (rc) pr_err("setup_counters failed, rc:%d\n", rc); } end: mutex_unlock(&rsc->client_lock); kfree(input); return count; } static ssize_t _sde_debugfs_mode_ctrl_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; char buffer[MAX_BUFFER_SIZE]; int blen = 0; size_t max_size = min_t(size_t, count, MAX_BUFFER_SIZE); if (*ppos || !rsc || !rsc->hw_ops.mode_ctrl) return 0; mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); blen = scnprintf(buffer, max_size, "hw state is not supported during idle pc\n"); goto end; } blen = rsc->hw_ops.mode_ctrl(rsc, MODE_READ, buffer, max_size, 0); end: mutex_unlock(&rsc->client_lock); if (blen <= 0) return 0; if (blen > count) { blen = count; buffer[count - 1] = '\0'; } if (copy_to_user(buf, buffer, blen)) return -EFAULT; *ppos += blen; return blen; } static ssize_t _sde_debugfs_mode_ctrl_write(struct file *file, const char __user *p, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; char *input; u32 mode_state = 0; int rc; if (!rsc || !rsc->hw_ops.mode_ctrl || !count || count > MAX_COUNT_SIZE_SUPPORTED) return 0; input = kmalloc(count + 1, GFP_KERNEL); if (!input) return -ENOMEM; if (copy_from_user(input, p, count)) { kfree(input); return -EFAULT; } input[count] = '\0'; rc = kstrtoint(input, 0, &mode_state); if (rc) { pr_err("mode_state: int conversion failed rc:%d\n", rc); goto end; } pr_debug("mode_state: %d\n", mode_state); mode_state &= 0x7; if (mode_state != ALL_MODES_DISABLED && mode_state != ALL_MODES_ENABLED && mode_state != ONLY_MODE_0_ENABLED && mode_state != ONLY_MODE_0_1_ENABLED) { pr_err("invalid mode:%d combination\n", mode_state); goto end; } mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); goto state_check; } rsc->hw_ops.mode_ctrl(rsc, MODE_UPDATE, NULL, 0, mode_state); state_check: mutex_unlock(&rsc->client_lock); end: kfree(input); return count; } static ssize_t _sde_debugfs_vsync_mode_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; char buffer[MAX_BUFFER_SIZE]; int blen = 0; size_t max_size = min_t(size_t, count, MAX_BUFFER_SIZE); if (*ppos || !rsc || !rsc->hw_ops.hw_vsync) return 0; mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); blen = scnprintf(buffer, max_size, "hw state is not supported during idle pc\n"); goto end; } blen = rsc->hw_ops.hw_vsync(rsc, VSYNC_READ, buffer, max_size, 0); end: mutex_unlock(&rsc->client_lock); if (blen <= 0) return 0; if (blen > count) { blen = count; buffer[count - 1] = '\0'; } if (copy_to_user(buf, buffer, blen)) return -EFAULT; *ppos += blen; return blen; } static ssize_t _sde_debugfs_vsync_mode_write(struct file *file, const char __user *p, size_t count, loff_t *ppos) { struct sde_rsc_priv *rsc = file->private_data; char *input; u32 vsync_state = 0; int rc; if (!rsc || !rsc->hw_ops.hw_vsync || !count || count > MAX_COUNT_SIZE_SUPPORTED) return 0; input = kmalloc(count + 1, GFP_KERNEL); if (!input) return -ENOMEM; if (copy_from_user(input, p, count)) { kfree(input); return -EFAULT; } input[count] = '\0'; rc = kstrtoint(input, 0, &vsync_state); if (rc) { pr_err("vsync_state: int conversion failed rc:%d\n", rc); goto end; } pr_debug("vsync_state: %d\n", vsync_state); vsync_state &= 0x7; mutex_lock(&rsc->client_lock); if (rsc->current_state == SDE_RSC_IDLE_STATE) { pr_debug("debug node is not supported during idle state\n"); goto state_check; } if (vsync_state) rsc->hw_ops.hw_vsync(rsc, VSYNC_ENABLE, NULL, 0, vsync_state - 1); else rsc->hw_ops.hw_vsync(rsc, VSYNC_DISABLE, NULL, 0, 0); state_check: mutex_unlock(&rsc->client_lock); end: kfree(input); return count; } static const struct file_operations debugfs_status_fops = { .open = _sde_debugfs_status_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations mode_control_fops = { .open = _sde_debugfs_generic_noseek_open, .read = _sde_debugfs_mode_ctrl_read, .write = _sde_debugfs_mode_ctrl_write, }; static const struct file_operations vsync_status_fops = { .open = _sde_debugfs_generic_noseek_open, .read = _sde_debugfs_vsync_mode_read, .write = _sde_debugfs_vsync_mode_write, }; static const struct file_operations profiling_enable_fops = { .open = _sde_debugfs_generic_noseek_open, .read = _sde_debugfs_profiling_read, .write = _sde_debugfs_profiling_write, }; static const struct file_operations profiling_counts_fops = { .open = _sde_debugfs_counters_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name) { rsc->debugfs_root = debugfs_create_dir(name, NULL); if (!rsc->debugfs_root) return; /* don't error check these */ debugfs_create_file("status", 0400, rsc->debugfs_root, rsc, &debugfs_status_fops); debugfs_create_file("mode_control", 0600, rsc->debugfs_root, rsc, &mode_control_fops); debugfs_create_file("vsync_mode", 0600, rsc->debugfs_root, rsc, &vsync_status_fops); if (rsc->profiling_supp) { debugfs_create_file("profiling_en", 0600, rsc->debugfs_root, rsc, &profiling_enable_fops); debugfs_create_file("profiling_counts", 0400, rsc->debugfs_root, rsc, &profiling_counts_fops); } debugfs_create_x32("debug_mode", 0600, rsc->debugfs_root, &rsc->debug_mode); } #else static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name) { } #endif /* defined(CONFIG_DEBUG_FS) */ static void sde_rsc_deinit(struct platform_device *pdev, struct sde_rsc_priv *rsc) { if (!rsc) return; sde_rsc_resource_disable(rsc); if (rsc->sw_fs_enabled) regulator_disable(rsc->fs); if (rsc->fs) devm_regulator_put(rsc->fs); if (rsc->wrapper_io.base) msm_dss_iounmap(&rsc->wrapper_io); if (rsc->drv_io.base) msm_dss_iounmap(&rsc->drv_io); sde_power_resource_deinit(pdev, &rsc->phandle); debugfs_remove_recursive(rsc->debugfs_root); kfree(rsc); } /** * sde_rsc_get_io_resources - collect register ranges for the device to * perform access control on TUI transition * @io_res: io resource list * @data: payload data provided during msm_register_vm_event * Returns: zero on success */ static int sde_rsc_get_io_resources(struct msm_io_res *io_res, void *data) { int rc = 0; struct sde_rsc_priv *rsc = (struct sde_rsc_priv *)data; struct platform_device *pdev = to_platform_device(rsc->dev); rc = msm_dss_get_io_mem(pdev, &io_res->mem); if (rc) { pr_err("failed to get rsc io mem, rc = %d\n", rc); return rc; } return 0; } /** * sde_rsc_bind - bind rsc device with controlling device * @dev: Pointer to base of platform device * @master: Pointer to container of drm device * @data: Pointer to private data * Returns: Zero on success */ static int sde_rsc_bind(struct device *dev, struct device *master, void *data) { struct sde_rsc_priv *rsc; struct drm_device *drm; struct platform_device *pdev = to_platform_device(dev); struct msm_vm_ops vm_event_ops = { .vm_get_io_resources = sde_rsc_get_io_resources, }; if (!dev || !pdev || !master) { pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n", dev, pdev, master); return -EINVAL; } drm = dev_get_drvdata(master); rsc = platform_get_drvdata(pdev); if (!drm || !rsc) { pr_err("invalid param(s), drm %pK, rsc %pK\n", drm, rsc); return -EINVAL; } sde_dbg_reg_register_base(SDE_RSC_DRV_DBG_NAME, rsc->drv_io.base, rsc->drv_io.len, msm_get_phys_addr(pdev, "drv"), SDE_DBG_RSC); sde_dbg_reg_register_base(SDE_RSC_WRAPPER_DBG_NAME, rsc->wrapper_io.base, rsc->wrapper_io.len, msm_get_phys_addr(pdev, "wrapper"), SDE_DBG_RSC); msm_register_vm_event(master, dev, &vm_event_ops, (void *)rsc); return 0; } /** * sde_rsc_unbind - unbind rsc from controlling device * @dev: Pointer to base of platform device * @master: Pointer to container of drm device * @data: Pointer to private data */ static void sde_rsc_unbind(struct device *dev, struct device *master, void *data) { struct sde_rsc_priv *rsc; struct platform_device *pdev = to_platform_device(dev); if (!dev || !pdev) { pr_err("invalid param(s)\n"); return; } rsc = platform_get_drvdata(pdev); if (!rsc) { pr_err("invalid display rsc\n"); return; } msm_unregister_vm_event(master, dev); } static const struct component_ops sde_rsc_comp_ops = { .bind = sde_rsc_bind, .unbind = sde_rsc_unbind, }; static int sde_rsc_probe(struct platform_device *pdev) { int ret; struct sde_rsc_priv *rsc; static int counter; char name[MAX_RSC_CLIENT_NAME_LEN]; if (counter >= MAX_RSC_COUNT) { pr_err("sde rsc supports probe till MAX_RSC_COUNT=%d devices\n", MAX_RSC_COUNT); return -EINVAL; } rsc = kzalloc(sizeof(*rsc), GFP_KERNEL); if (!rsc) { ret = -ENOMEM; goto rsc_alloc_fail; } platform_set_drvdata(pdev, rsc); rsc->dev = &pdev->dev; of_property_read_u32(pdev->dev.of_node, "qcom,sde-rsc-version", &rsc->version); switch (rsc->version) { case SDE_RSC_REV_1: rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V1; break; case SDE_RSC_REV_2: rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V2; break; case SDE_RSC_REV_3: rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V3; break; case SDE_RSC_REV_4: rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V4; break; case SDE_RSC_REV_5: default: rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V5; break; } if (rsc->version >= SDE_RSC_REV_3) { rsc->time_slot_0_ns = rsc->single_tcs_execution_time + RSC_MODE_INSTRUCTION_TIME; rsc->backoff_time_ns = RSC_MODE_INSTRUCTION_TIME; rsc->mode_threshold_time_ns = rsc->time_slot_0_ns; } else { rsc->time_slot_0_ns = (rsc->single_tcs_execution_time * 2) + RSC_MODE_INSTRUCTION_TIME; rsc->backoff_time_ns = rsc->single_tcs_execution_time + RSC_MODE_INSTRUCTION_TIME; rsc->mode_threshold_time_ns = rsc->backoff_time_ns + RSC_MODE_THRESHOLD_OVERHEAD; } if (rsc->version >= SDE_RSC_REV_4) rsc->profiling_supp = true; ret = sde_power_resource_init(pdev, &rsc->phandle); if (ret) { pr_err("sde rsc:power resource init failed ret:%d\n", ret); goto sde_rsc_fail; } rsc->rpmh_dev = rpmh_dev[SDE_RSC_INDEX + counter]; if (IS_ERR_OR_NULL(rsc->rpmh_dev)) { ret = !rsc->rpmh_dev ? -EINVAL : PTR_ERR(rsc->rpmh_dev); rsc->rpmh_dev = NULL; pr_err("rpmh device node is not available ret:%d\n", ret); goto sde_rsc_fail; } ret = msm_dss_ioremap_byname(pdev, &rsc->wrapper_io, "wrapper"); if (ret) { pr_err("sde rsc: wrapper io data mapping failed ret=%d\n", ret); goto sde_rsc_fail; } ret = msm_dss_ioremap_byname(pdev, &rsc->drv_io, "drv"); if (ret) { pr_err("sde rsc: drv io data mapping failed ret:%d\n", ret); goto sde_rsc_fail; } rsc->fs = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR_OR_NULL(rsc->fs)) { rsc->fs = NULL; pr_err("unable to get regulator\n"); goto sde_rsc_fail; } if (rsc->version >= SDE_RSC_REV_3) ret = sde_rsc_hw_register_v3(rsc); else ret = sde_rsc_hw_register(rsc); if (ret) { pr_err("sde rsc: hw register failed ret:%d\n", ret); goto sde_rsc_fail; } ret = regulator_enable(rsc->fs); if (ret) { pr_err("sde rsc: fs on failed ret:%d\n", ret); goto sde_rsc_fail; } rsc->sw_fs_enabled = true; ret = sde_rsc_resource_enable(rsc); if (ret < 0) { pr_err("failed to enable sde rsc power resources rc:%d\n", ret); goto sde_rsc_fail; } if (sde_rsc_timer_calculate(rsc, NULL, SDE_RSC_IDLE_STATE)) goto sde_rsc_fail; sde_rsc_resource_disable(rsc); INIT_LIST_HEAD(&rsc->client_list); INIT_LIST_HEAD(&rsc->event_list); mutex_init(&rsc->client_lock); init_waitqueue_head(&rsc->rsc_vsync_waitq); atomic_set(&rsc->resource_refcount, 0); pr_info("sde rsc index:%d probed successfully\n", SDE_RSC_INDEX + counter); rsc_prv_list[SDE_RSC_INDEX + counter] = rsc; snprintf(name, MAX_RSC_CLIENT_NAME_LEN, "%s%d", "sde_rsc", counter); _sde_rsc_init_debugfs(rsc, name); counter++; ret = component_add(&pdev->dev, &sde_rsc_comp_ops); if (ret) pr_debug("component add failed, ret=%d\n", ret); ret = 0; return ret; sde_rsc_fail: sde_rsc_deinit(pdev, rsc); rsc_alloc_fail: return ret; } static int sde_rsc_remove(struct platform_device *pdev) { struct sde_rsc_priv *rsc = platform_get_drvdata(pdev); sde_rsc_deinit(pdev, rsc); return 0; } static int sde_rsc_rpmh_probe(struct platform_device *pdev) { int ret = 0; uint32_t index = 0; ret = of_property_read_u32(pdev->dev.of_node, "cell-index", &index); if (ret) { pr_err("unable to find sde rsc cell index\n"); return ret; } else if (index >= MAX_RSC_COUNT) { pr_err("invalid cell index for sde rsc:%d\n", index); return -EINVAL; } rpmh_dev[index] = &pdev->dev; return 0; } int sde_rsc_rpmh_remove(struct platform_device *pdev) { int i; for (i = 0; i < MAX_RSC_COUNT; i++) rpmh_dev[i] = NULL; return 0; } static const struct of_device_id dt_match[] = { { .compatible = "qcom,sde-rsc"}, {}, }; static struct platform_driver sde_rsc_platform_driver = { .probe = sde_rsc_probe, .remove = sde_rsc_remove, .driver = { .name = "sde_rsc", .of_match_table = dt_match, .suppress_bind_attrs = true, }, }; static const struct of_device_id sde_rsc_rpmh_match[] = { {.compatible = "qcom,sde-rsc-rpmh"}, {}, }; static struct platform_driver sde_rsc_rpmh_driver = { .probe = sde_rsc_rpmh_probe, .remove = sde_rsc_rpmh_remove, .driver = { .name = "sde_rsc_rpmh", .of_match_table = sde_rsc_rpmh_match, }, }; void __init sde_rsc_register(void) { platform_driver_register(&sde_rsc_platform_driver); } void __exit sde_rsc_unregister(void) { platform_driver_unregister(&sde_rsc_platform_driver); } void __init sde_rsc_rpmh_register(void) { platform_driver_register(&sde_rsc_rpmh_driver); }