| /* Copyright (c) 2015-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. |
| * |
| */ |
| |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/errno.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/delay.h> |
| #include <linux/sort.h> |
| #include <linux/clk.h> |
| #include <linux/bitmap.h> |
| |
| #include "sde_rotator_r1_hwio.h" |
| #include "sde_rotator_util.h" |
| #include "sde_rotator_r1_internal.h" |
| #include "sde_rotator_core.h" |
| |
| struct sde_mdp_ctl *sde_mdp_ctl_alloc(struct sde_rot_data_type *mdata, |
| u32 off) |
| { |
| struct sde_mdp_ctl *ctl = NULL; |
| static struct sde_mdp_ctl sde_ctl[5]; |
| static const u32 offset[] = {0x00002000, 0x00002200, 0x00002400, |
| 0x00002600, 0x00002800}; |
| |
| if (off >= ARRAY_SIZE(offset)) { |
| SDEROT_ERR("invalid parameters\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| ctl = &sde_ctl[off]; |
| ctl->mdata = mdata; |
| ctl->num = off; |
| ctl->offset = offset[ctl->num]; |
| ctl->base = mdata->sde_io.base + ctl->offset; |
| return ctl; |
| } |
| |
| int sde_mdp_ctl_free(struct sde_mdp_ctl *ctl) |
| { |
| if (!ctl) |
| return -ENODEV; |
| |
| if (ctl->wb) |
| sde_mdp_wb_free(ctl->wb); |
| |
| ctl->is_secure = false; |
| ctl->mixer_left = NULL; |
| ctl->mixer_right = NULL; |
| ctl->wb = NULL; |
| memset(&ctl->ops, 0, sizeof(ctl->ops)); |
| |
| return 0; |
| } |
| |
| struct sde_mdp_mixer *sde_mdp_mixer_assign(u32 id, bool wb) |
| { |
| struct sde_mdp_mixer *mixer = NULL; |
| struct sde_rot_data_type *mdata = sde_rot_get_mdata(); |
| static struct sde_mdp_mixer sde_mixer[16]; |
| static const u32 offset[] = {0x00048000, 0x00049000}; |
| |
| if (id >= ARRAY_SIZE(offset)) { |
| SDEROT_ERR("invalid parameters\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| mixer = &sde_mixer[id]; |
| mixer->num = id; |
| mixer->offset = offset[mixer->num]; |
| mixer->base = mdata->sde_io.base + mixer->offset; |
| return mixer; |
| } |
| |
| static void sde_mdp_mixer_setup(struct sde_mdp_ctl *master_ctl, |
| int mixer_mux) |
| { |
| int i; |
| struct sde_mdp_ctl *ctl = NULL; |
| struct sde_mdp_mixer *mixer = sde_mdp_mixer_get(master_ctl, |
| mixer_mux); |
| |
| if (!mixer) |
| return; |
| |
| ctl = mixer->ctl; |
| if (!ctl) |
| return; |
| |
| /* check if mixer setup for rotator is needed */ |
| if (mixer->rotator_mode) { |
| int nmixers = 5; |
| |
| for (i = 0; i < nmixers; i++) |
| sde_mdp_ctl_write(ctl, SDE_MDP_REG_CTL_LAYER(i), 0); |
| return; |
| } |
| } |
| |
| struct sde_mdp_mixer *sde_mdp_mixer_get(struct sde_mdp_ctl *ctl, int mux) |
| { |
| struct sde_mdp_mixer *mixer = NULL; |
| |
| if (!ctl) { |
| SDEROT_ERR("ctl not initialized\n"); |
| return NULL; |
| } |
| |
| switch (mux) { |
| case SDE_MDP_MIXER_MUX_DEFAULT: |
| case SDE_MDP_MIXER_MUX_LEFT: |
| mixer = ctl->mixer_left; |
| break; |
| case SDE_MDP_MIXER_MUX_RIGHT: |
| mixer = ctl->mixer_right; |
| break; |
| } |
| |
| return mixer; |
| } |
| |
| int sde_mdp_get_pipe_flush_bits(struct sde_mdp_pipe *pipe) |
| { |
| u32 flush_bits; |
| |
| if (pipe->type == SDE_MDP_PIPE_TYPE_DMA) |
| flush_bits |= BIT(pipe->num) << 5; |
| else if (pipe->num == SDE_MDP_SSPP_VIG3 || |
| pipe->num == SDE_MDP_SSPP_RGB3) |
| flush_bits |= BIT(pipe->num) << 10; |
| else if (pipe->type == SDE_MDP_PIPE_TYPE_CURSOR) |
| flush_bits |= BIT(22 + pipe->num - SDE_MDP_SSPP_CURSOR0); |
| else /* RGB/VIG 0-2 pipes */ |
| flush_bits |= BIT(pipe->num); |
| |
| return flush_bits; |
| } |
| |
| int sde_mdp_mixer_pipe_update(struct sde_mdp_pipe *pipe, |
| struct sde_mdp_mixer *mixer, int params_changed) |
| { |
| struct sde_mdp_ctl *ctl; |
| |
| if (!pipe) |
| return -EINVAL; |
| if (!mixer) |
| return -EINVAL; |
| ctl = mixer->ctl; |
| if (!ctl) |
| return -EINVAL; |
| |
| ctl->flush_bits |= sde_mdp_get_pipe_flush_bits(pipe); |
| return 0; |
| } |
| |
| int sde_mdp_display_wait4comp(struct sde_mdp_ctl *ctl) |
| { |
| int ret; |
| |
| if (!ctl) { |
| SDEROT_ERR("invalid ctl\n"); |
| return -ENODEV; |
| } |
| |
| if (ctl->ops.wait_fnc) |
| ret = ctl->ops.wait_fnc(ctl, NULL); |
| |
| return ret; |
| } |
| |
| int sde_mdp_display_commit(struct sde_mdp_ctl *ctl, void *arg, |
| struct sde_mdp_commit_cb *commit_cb) |
| { |
| int ret = 0; |
| u32 ctl_flush_bits = 0; |
| |
| if (!ctl) { |
| SDEROT_ERR("display function not set\n"); |
| return -ENODEV; |
| } |
| |
| if (ctl->ops.prepare_fnc) |
| ret = ctl->ops.prepare_fnc(ctl, arg); |
| |
| if (ret) { |
| SDEROT_ERR("error preparing display\n"); |
| goto done; |
| } |
| |
| sde_mdp_mixer_setup(ctl, SDE_MDP_MIXER_MUX_LEFT); |
| sde_mdp_mixer_setup(ctl, SDE_MDP_MIXER_MUX_RIGHT); |
| |
| sde_mdp_ctl_write(ctl, SDE_MDP_REG_CTL_TOP, ctl->opmode); |
| ctl->flush_bits |= BIT(17); /* CTL */ |
| |
| ctl_flush_bits = ctl->flush_bits; |
| |
| sde_mdp_ctl_write(ctl, SDE_MDP_REG_CTL_FLUSH, ctl_flush_bits); |
| /* ensure the flush command is issued after the barrier */ |
| wmb(); |
| ctl->flush_reg_data = ctl_flush_bits; |
| ctl->flush_bits = 0; |
| if (ctl->ops.display_fnc) |
| ret = ctl->ops.display_fnc(ctl, arg); /* DSI0 kickoff */ |
| if (ret) |
| SDEROT_WARN("ctl %d error displaying frame\n", ctl->num); |
| |
| done: |
| return ret; |
| } |
| |
| /** |
| * @sde_mdp_ctl_mixer_switch() - return ctl mixer of @return_type |
| * @ctl: Pointer to ctl structure to be switched. |
| * @return_type: wb_type of the ctl to be switched to. |
| * |
| * Virtual mixer switch should be performed only when there is no |
| * dedicated wfd block and writeback block is shared. |
| */ |
| struct sde_mdp_ctl *sde_mdp_ctl_mixer_switch(struct sde_mdp_ctl *ctl, |
| u32 return_type) |
| { |
| if (ctl->wb_type == return_type) |
| return ctl; |
| |
| SDEROT_ERR("unable to switch mixer to type=%d\n", return_type); |
| return NULL; |
| } |
| |
| struct sde_mdp_writeback *sde_mdp_wb_assign(u32 num, u32 reg_index) |
| { |
| struct sde_rot_data_type *mdata = sde_rot_get_mdata(); |
| struct sde_mdp_writeback *wb = NULL; |
| static struct sde_mdp_writeback sde_wb[16]; |
| static const u32 offset[] = {0x00065000, 0x00065800, 0x00066000}; |
| |
| if (num >= ARRAY_SIZE(offset)) { |
| SDEROT_ERR("invalid parameters\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| wb = &sde_wb[num]; |
| wb->num = num; |
| wb->offset = offset[wb->num]; |
| if (!wb) |
| return NULL; |
| |
| wb->base = mdata->sde_io.base; |
| wb->base += wb->offset; |
| return wb; |
| } |
| |
| void sde_mdp_wb_free(struct sde_mdp_writeback *wb) |
| { |
| } |