| /* Copyright (c) 2015-2018, 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. |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/err.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/platform_device.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/spinlock.h> |
| #include <linux/iommu.h> |
| #include <linux/msm_ion.h> |
| #include <linux/msm-bus.h> |
| #include <linux/msm-bus-board.h> |
| #include <media/videobuf2-core.h> |
| |
| #include "msm_camera_io_util.h" |
| #include "cam_smmu_api.h" |
| #include "msm_jpeg_dma_dev.h" |
| #include "msm_jpeg_dma_hw.h" |
| #include "msm_jpeg_dma_regs.h" |
| |
| /* Jpeg dma scale unity */ |
| #define MSM_JPEGDMA_SCALE_UNI (1 << 21) |
| /* Jpeg dma bw numerator */ |
| #define MSM_JPEGDMA_BW_NUM 38 |
| /* Jpeg dma bw denominator */ |
| #define MSM_JPEGDMA_BW_DEN 10 |
| /* Jpeg bus client name */ |
| #define MSM_JPEGDMA_BUS_CLIENT_NAME "msm_jpeg_dma" |
| /* Jpeg dma engine timeout in ms */ |
| #define MSM_JPEGDMA_TIMEOUT_MS 500 |
| /* Jpeg dma smmu name */ |
| #define MSM_JPEGDMA_SMMU_NAME "jpeg_dma" |
| |
| static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = { |
| { |
| .div = 0x3C0000, |
| .width = 256, |
| .reg_val = 4, |
| }, |
| { |
| .div = 0x7C0000, |
| .width = 128, |
| .reg_val = 3, |
| }, |
| { |
| .div = 0xFC0000, |
| .width = 64, |
| .reg_val = 2, |
| }, |
| { |
| .div = 0x1FC0000, |
| .width = 32, |
| .reg_val = 1, |
| }, |
| { |
| .div = 0x4000000, |
| .width = 16, |
| .reg_val = 0, |
| }, |
| }; |
| |
| /* |
| * jpegdma_do_div - long division. |
| * @num: dividend |
| * @den: divisor |
| * returns quotient value. |
| */ |
| static inline long long jpegdma_do_div(long long num, long long den) |
| { |
| do_div(num, den); |
| return num; |
| } |
| |
| /* |
| * msm_jpegdma_hw_read_reg - dma read from register. |
| * @dma: Pointer to dma device. |
| * @base_idx: dma memory resource index. |
| * @reg: Register addr need to be read from. |
| */ |
| static inline u32 msm_jpegdma_hw_read_reg(struct msm_jpegdma_device *dma, |
| enum msm_jpegdma_mem_resources base_idx, u32 reg) |
| { |
| return msm_camera_io_r(dma->iomem_base[base_idx] + reg); |
| } |
| |
| /* |
| * msm_jpegdma_hw_write_reg - dma write to register. |
| * @dma: Pointer to dma device. |
| * @base_idx: dma memory resource index. |
| * @reg: Register addr need to be read from. |
| * @value: Value to be written. |
| */ |
| static inline void msm_jpegdma_hw_write_reg(struct msm_jpegdma_device *dma, |
| enum msm_jpegdma_mem_resources base_idx, u32 reg, u32 value) |
| { |
| pr_debug("%s:%d]%pK %08x\n", __func__, __LINE__, |
| dma->iomem_base[base_idx] + reg, |
| value); |
| msm_camera_io_w(value, dma->iomem_base[base_idx] + reg); |
| } |
| |
| /* |
| * msm_jpegdma_hw_enable_irq - Enable dma interrupts. |
| * @dma: Pointer to dma device. |
| */ |
| static void msm_jpegdma_hw_enable_irq(struct msm_jpegdma_device *dma) |
| { |
| u32 reg; |
| |
| reg = MSM_JPEGDMA_IRQ_MASK_SESSION_DONE | |
| MSM_JPEGDMA_IRQ_MASK_AXI_HALT | |
| MSM_JPEGDMA_IRQ_MASK_RST_DONE; |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_IRQ_MASK_ADDR, reg); |
| } |
| |
| /* |
| * msm_jpegdma_hw_disable_irq - Disable dma interrupts. |
| * @dma: Pointer to dma device. |
| */ |
| static void msm_jpegdma_hw_disable_irq(struct msm_jpegdma_device *dma) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_IRQ_MASK_ADDR, 0); |
| } |
| |
| /* |
| * msm_jpegdma_hw_clear_irq - Clear dma interrupts. |
| * @dma: Pointer to dma device. |
| * @status: Status to clear. |
| */ |
| static void msm_jpegdma_hw_clear_irq(struct msm_jpegdma_device *dma, |
| u32 status) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_IRQ_CLEAR_ADDR, status); |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_irq_status - Get dma irq status |
| * @dma: Pointer to dma device. |
| */ |
| static u32 msm_jpegdma_hw_get_irq_status(struct msm_jpegdma_device *dma) |
| { |
| return msm_jpegdma_hw_read_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_IRQ_STATUS); |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_num_pipes - Get number of dma pipes |
| * @dma: Pointer to dma device. |
| */ |
| static int msm_jpegdma_hw_get_num_pipes(struct msm_jpegdma_device *dma) |
| { |
| int num_pipes; |
| u32 reg; |
| |
| reg = msm_jpegdma_hw_read_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_HW_CAPABILITY); |
| |
| num_pipes = (reg & MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_BMSK) >> |
| MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_SHFT; |
| |
| return num_pipes; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_clock_index - Get clock index by name |
| * @dma: Pointer to dma device. |
| * @clk_name: clock name. |
| */ |
| static int msm_jpegdma_hw_get_clock_index(struct msm_jpegdma_device *dma, |
| const char *clk_name) |
| { |
| uint32_t i = 0; |
| |
| for (i = 0; i < dma->num_clk; i++) { |
| if (!strcmp(clk_name, dma->jpeg_clk_info[i].clk_name)) |
| return i; |
| } |
| return -EINVAL; |
| } |
| |
| /* |
| * msm_jpegdma_hw_reset - Reset jpeg dma core. |
| * @dma: Pointer to dma device. |
| */ |
| static int msm_jpegdma_hw_reset(struct msm_jpegdma_device *dma) |
| { |
| unsigned long time; |
| |
| init_completion(&dma->hw_reset_completion); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_HW_JPEGDMA_RESET, MSM_HW_JPEGDMA_RESET_DEFAULT); |
| |
| time = wait_for_completion_timeout(&dma->hw_reset_completion, |
| msecs_to_jiffies(MSM_JPEGDMA_TIMEOUT_MS)); |
| if (!time) { |
| dev_err(dma->dev, "Jpeg dma detection reset timeout\n"); |
| return -ETIME; |
| } |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_halt - Halt jpeg dma core. |
| * @dma: Pointer to dma device. |
| */ |
| static int msm_jpegdma_hw_halt(struct msm_jpegdma_device *dma) |
| { |
| unsigned long time; |
| |
| init_completion(&dma->hw_halt_completion); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_CMD_ADDR, 0x4); |
| |
| time = wait_for_completion_timeout(&dma->hw_halt_completion, |
| msecs_to_jiffies(MSM_JPEGDMA_TIMEOUT_MS)); |
| if (!time) { |
| dev_err(dma->dev, "Jpeg dma detection halt timeout\n"); |
| return -ETIME; |
| } |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_run - Enable dma processing. |
| * @dma: Pointer to dma device. |
| */ |
| static int msm_jpegdma_hw_run(struct msm_jpegdma_device *dma) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_CMD_ADDR, 0x1); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_core_config - Set jpeg dma core configuration. |
| * @dma: Pointer to dma device. |
| * @num_pipes: Number of pipes. |
| * @scale_0: Scaler 0 enable. |
| * @scale_1: Scaler 1 enable. |
| */ |
| static int msm_jpegdma_hw_core_config(struct msm_jpegdma_device *dma, |
| int num_pipes, int scale_0, int scale_1) |
| { |
| u32 reg; |
| |
| reg = (scale_0 << MSM_JPEGDMA_CORE_CFG_SCALE_0_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_TEST_BUS_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_BRIDGE_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_WE_0_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_FE_0_ENABLE_SHFT); |
| |
| /* Enable read write ports for second pipe */ |
| if (num_pipes > 1) { |
| reg |= (scale_1 << MSM_JPEGDMA_CORE_CFG_SCALE_1_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_WE_1_ENABLE_SHFT) | |
| (0x1 << MSM_JPEGDMA_CORE_CFG_FE_1_ENABLE_SHFT); |
| } |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_CORE_CFG_ADDR, reg); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_0_block - Fetch engine 0 block configuration. |
| * @dma: Pointer to dma device. |
| * @block_config: Pointer to block configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_fe_0_block(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_block_config *block_config, |
| enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| switch (plane_type) { |
| case JPEGDMA_PLANE_TYPE_Y: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_Y << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CB: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CB << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CR: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CR << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CBCR: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CBCR << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| default: |
| dev_err(dma->dev, "Unsupported plane type %d\n", plane_type); |
| return -EINVAL; |
| } |
| |
| reg |= (block_config->block.reg_val << |
| MSM_JPEGDMA_FE_CFG_BLOCK_WIDTH_SHFT) | |
| (0x1 << MSM_JPEGDMA_FE_CFG_MAL_BOUNDARY_SHFT) | |
| (0x1 << MSM_JPEGDMA_FE_CFG_MAL_EN_SHFT) | |
| (0xF << MSM_JPEGDMA_FE_CFG_BURST_LENGTH_MAX_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_0_CFG_ADDR, reg); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_1_block - Fetch engine 1 block configuration. |
| * @dma: Pointer to dma device. |
| * @block_config: Pointer to block configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_fe_1_block(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_block_config *block_config, |
| enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| switch (plane_type) { |
| case JPEGDMA_PLANE_TYPE_Y: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_Y << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CB: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CB << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CR: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CR << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| case JPEGDMA_PLANE_TYPE_CBCR: |
| reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CBCR << |
| MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT; |
| break; |
| default: |
| dev_err(dma->dev, "Unsupported plane type %d\n", plane_type); |
| return -EINVAL; |
| } |
| |
| reg |= (block_config->block.reg_val << |
| MSM_JPEGDMA_FE_CFG_BLOCK_WIDTH_SHFT) | |
| (0xF << MSM_JPEGDMA_FE_CFG_BURST_LENGTH_MAX_SHFT) | |
| (0x1 << MSM_JPEGDMA_FE_CFG_MAL_EN_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_1_CFG_ADDR, reg); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_0_phase - Fetch engine 0 phase configuration. |
| * @dma: Pointer to dma device. |
| * @phase: Fetch engine 0 phase. |
| */ |
| static int msm_jpegdma_hw_fe_0_phase(struct msm_jpegdma_device *dma, int phase) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_HINIT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, phase); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_1_phase - Fetch engine 1 phase configuration. |
| * @dma: Pointer to dma device. |
| * @phase: Fetch engine 1 phase. |
| */ |
| static int msm_jpegdma_hw_fe_1_phase(struct msm_jpegdma_device *dma, int phase) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_HINIT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, 0x00); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, phase); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_0_size - Fetch engine 0 size configuration. |
| * @dma: Pointer to dma device. |
| * @size: Pointer to size configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_fe_0_size(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size *size, enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| reg = (size->width + size->left - 1) | |
| ((size->height + size->top - 1) << |
| MSM_JPEGDMA_FE_RD_BUFFER_SIZE_HEIGHT_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_BUFFER_SIZE_0_ADDR, reg); |
| |
| if (size->left && plane_type == JPEGDMA_PLANE_TYPE_CBCR) |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, size->left / 2); |
| else |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, size->left); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, size->top); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_STRIDE_ADDR, size->stride); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_1_size - Fetch engine 1 size configuration. |
| * @dma: Pointer to dma device. |
| * @size: Pointer to size configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_fe_1_size(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size *size, enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| reg = (size->width + size->left - 1) | |
| ((size->height + size->top - 1) << |
| MSM_JPEGDMA_FE_RD_BUFFER_SIZE_HEIGHT_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_BUFFER_SIZE_1_ADDR, reg); |
| |
| if (size->left && plane_type == JPEGDMA_PLANE_TYPE_CBCR) |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, size->left / 2); |
| else |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, size->left); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, size->top); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_STRIDE_ADDR, size->stride); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_0_addr - Set fetch engine 0 address. |
| * @dma: Pointer to dma device. |
| * @addr: Fetch engine address. |
| */ |
| static int msm_jpegdma_hw_fe_0_addr(struct msm_jpegdma_device *dma, u32 addr) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_CMD_ADDR, MSM_JPEGDMA_CMD_CLEAR_READ_PLN_QUEUES); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_0_PNTR_ADDR, addr); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_1_addr - Set fetch engine 1 address. |
| * @dma: Pointer to dma device. |
| * @addr: Fetch engine address. |
| */ |
| static int msm_jpegdma_hw_fe_1_addr(struct msm_jpegdma_device *dma, u32 addr) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_FE_RD_1_PNTR_ADDR, addr); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_fe_0_block - Write engine 0 block configuration. |
| * @dma: Pointer to dma device. |
| * @block_config: Pointer to block configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_we_0_block(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_block_config *block, |
| enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| reg = (0xF << MSM_JPEGDMA_WE_CFG_BURST_LENGTH_MAX_SHFT) | |
| (0x1 << MSM_JPEGDMA_WE_CFG_MAL_BOUNDARY_SHFT) | |
| (0x1 << MSM_JPEGDMA_WE_CFG_MAL_EN_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_CFG_ADDR, reg); |
| |
| reg = ((block->blocks_per_row - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_0_BLOCKS_PER_ROW_SHFT) | |
| (block->blocks_per_col - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_CFG_0_ADDR, reg); |
| |
| reg = ((block->h_step_last - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_1_LAST_H_STEP_SHFT) | |
| (block->h_step - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_CFG_1_ADDR, reg); |
| |
| reg = ((block->v_step_last - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_2_LAST_V_STEP_SHFT) | |
| (block->v_step - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_CFG_2_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_CFG_3_ADDR, 0x0); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_we_1_block - Write engine 1 block configuration. |
| * @dma: Pointer to dma device. |
| * @block_config: Pointer to block configuration. |
| * @plane_type: Plane type. |
| */ |
| static int msm_jpegdma_hw_we_1_block(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_block_config *block, |
| enum msm_jpegdma_plane_type plane_type) |
| { |
| u32 reg; |
| |
| reg = ((block->blocks_per_row - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_0_BLOCKS_PER_ROW_SHFT) | |
| (block->blocks_per_col - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_CFG_0_ADDR, reg); |
| |
| reg = ((block->h_step_last - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_1_LAST_H_STEP_SHFT) | |
| (block->h_step - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_CFG_1_ADDR, reg); |
| |
| reg = ((block->v_step_last - 1) << |
| MSM_JPEGDMA_WE_PLN_WR_CFG_2_LAST_V_STEP_SHFT) | |
| (block->v_step - 1); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_CFG_2_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_CFG_3_ADDR, 0x0); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_we_0_size - Write engine 0 size configuration. |
| * @dma: Pointer to dma device. |
| * @size: Pointer to size configuration. |
| */ |
| static int msm_jpegdma_hw_we_0_size(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size *size) |
| { |
| u32 reg; |
| |
| reg = (size->width) | ((size->height) << |
| MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_HEIGHT_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_0_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_STRIDE_ADDR, size->stride); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_we_1_size - Write engine 1 size configuration. |
| * @dma: Pointer to dma device. |
| * @size: Pointer to size configuration. |
| */ |
| static int msm_jpegdma_hw_we_1_size(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size *size) |
| { |
| u32 reg; |
| |
| reg = (size->width) | ((size->height) << |
| MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_HEIGHT_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_1_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_STRIDE_ADDR, size->stride); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_we_0_addr - Set write engine 0 address. |
| * @dma: Pointer to dma device. |
| * @addr: Fetch engine address. |
| */ |
| static int msm_jpegdma_hw_we_0_addr(struct msm_jpegdma_device *dma, u32 addr) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_CMD_ADDR, MSM_JPEGDMA_CMD_CLEAR_WRITE_PLN_QUEUES); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_0_WR_PNTR_ADDR, addr); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_we_1_addr - Set write engine 1 address. |
| * @dma: Pointer to dma device. |
| * @addr: Fetch engine address. |
| */ |
| static int msm_jpegdma_hw_we_1_addr(struct msm_jpegdma_device *dma, u32 addr) |
| { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_WE_PLN_1_WR_PNTR_ADDR, addr); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_scale_0_config - Scale configuration for 0 pipeline. |
| * @dma: Pointer to dma device. |
| * @scale: Scale configuration. |
| */ |
| static int msm_jpegdma_hw_scale_0_config(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_scale *scale) |
| { |
| u32 reg; |
| u32 h_down_en; |
| u32 v_down_en; |
| |
| h_down_en = (scale->hor_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1; |
| v_down_en = (scale->ver_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1; |
| |
| reg = (h_down_en << MSM_JPEGDMA_PP_SCALE_CFG_HSCALE_ENABLE_SHFT) | |
| (v_down_en << MSM_JPEGDMA_PP_SCALE_CFG_VSCALE_ENABLE_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_0_SCALE_CFG_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_0_SCALE_PHASEH_STEP_ADDR, scale->hor_scale); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_0_SCALE_PHASEV_STEP_ADDR, scale->ver_scale); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_scale_1_config - Scale configuration for 1 pipeline. |
| * @dma: Pointer to dma device. |
| * @scale: Scale configuration. |
| */ |
| static int msm_jpegdma_hw_scale_1_config(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_scale *scale) |
| { |
| u32 reg; |
| u32 h_down_en; |
| u32 v_down_en; |
| |
| h_down_en = (scale->hor_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1; |
| v_down_en = (scale->ver_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1; |
| |
| reg = (h_down_en << MSM_JPEGDMA_PP_SCALE_CFG_HSCALE_ENABLE_SHFT) | |
| (v_down_en << MSM_JPEGDMA_PP_SCALE_CFG_VSCALE_ENABLE_SHFT); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_1_SCALE_CFG_ADDR, reg); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_1_SCALE_PHASEH_STEP_ADDR, scale->hor_scale); |
| |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_PP_1_SCALE_PHASEV_STEP_ADDR, scale->ver_scale); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_config_qos - Configure qos registers. |
| * @dma: Pointer to dma device. |
| */ |
| static void msm_jpegdma_hw_config_qos(struct msm_jpegdma_device *dma) |
| { |
| int i; |
| |
| if (!dma->qos_regs_num) |
| return; |
| |
| for (i = 0; i < dma->qos_regs_num; i++) |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| dma->qos_regs[i].reg, dma->qos_regs[i].val); |
| |
| } |
| |
| /* |
| * msm_jpegdma_hw_config_vbif - Configure and vbif interface. |
| * @dma: Pointer to dma device. |
| */ |
| static void msm_jpegdma_hw_config_vbif(struct msm_jpegdma_device *dma) |
| { |
| int i; |
| |
| if (!dma->vbif_regs_num) |
| return; |
| |
| for (i = 0; i < dma->vbif_regs_num; i++) |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_VBIF, |
| dma->vbif_regs[i].reg, dma->vbif_regs[i].val); |
| |
| } |
| |
| /* |
| * msm_jpegdma_hw_config_mmu_prefetch - Configure mmu prefetch registers. |
| * @dma: Pointer to dma device. |
| * @min_addr: Pointer to jpeg dma addr, containing min addrs of the plane. |
| * @max_addr: Pointer to jpeg dma addr, containing max addrs of the plane. |
| */ |
| static void msm_jpegdma_hw_config_mmu_prefetch(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_addr *min_addr, |
| struct msm_jpegdma_addr *max_addr) |
| { |
| int i; |
| |
| if (!dma->prefetch_regs_num) |
| return; |
| |
| for (i = 0; i < dma->prefetch_regs_num; i++) |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_VBIF, |
| dma->prefetch_regs[i].reg, dma->prefetch_regs[i].val); |
| |
| if (min_addr != NULL && max_addr != NULL) { |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN, min_addr->in_addr); |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX, max_addr->in_addr); |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN, min_addr->out_addr); |
| msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE, |
| MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX, max_addr->out_addr); |
| } |
| } |
| |
| /* |
| * msm_jpegdma_hw_calc_speed - Calculate speed based on framerate and size. |
| * @dma: Pointer to dma device. |
| * @size: Dma user size configuration. |
| * @speed: Calculated speed. |
| */ |
| static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size_config *size, |
| struct msm_jpegdma_speed *speed) |
| { |
| u64 width; |
| u64 height; |
| long real_clock; |
| u64 calc_rate; |
| int core_clk_idx; |
| |
| width = size->in_size.width + size->in_size.left; |
| height = size->in_size.height + size->in_size.top; |
| |
| calc_rate = (width * height * size->format.depth * size->fps) / 16; |
| core_clk_idx = msm_jpegdma_hw_get_clock_index(dma, |
| MSM_JPEGDMA_CORE_CLK); |
| if (core_clk_idx < 0) { |
| dev_err(dma->dev, "Can get clock index for dma %s\n", |
| MSM_JPEGDMA_CORE_CLK); |
| } |
| |
| real_clock = clk_round_rate(dma->clk[core_clk_idx], calc_rate); |
| if (real_clock < 0) { |
| dev_err(dma->dev, "Can not round core clock\n"); |
| return -EINVAL; |
| } |
| |
| speed->bus_ab = calc_rate * 2; |
| speed->bus_ib = jpegdma_do_div((real_clock * |
| (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)), |
| MSM_JPEGDMA_BW_DEN); |
| speed->core_clock = real_clock; |
| dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n", |
| speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps); |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_set_speed - Configure clock and bus bandwidth based on |
| * requested speed and dma clients. |
| * @size: Jpeg dma size configuration. |
| * @speed: Requested dma speed. |
| */ |
| static int msm_jpegdma_hw_set_speed(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size_config *size, |
| struct msm_jpegdma_speed *speed) |
| { |
| struct msm_jpegdma_speed new_sp; |
| struct msm_jpegdma_size_config new_size; |
| int ret; |
| int core_clk_idx; |
| |
| if (dma->active_clock_rate >= speed->core_clock) |
| return 0; |
| |
| new_sp = *speed; |
| if (dma->ref_count > 2) { |
| new_size = *size; |
| new_size.fps = size->fps * ((dma->ref_count + 1) / 2); |
| ret = msm_jpegdma_hw_calc_speed(dma, &new_size, &new_sp); |
| if (ret < 0) |
| return -EINVAL; |
| } |
| |
| core_clk_idx = msm_jpegdma_hw_get_clock_index(dma, |
| MSM_JPEGDMA_CORE_CLK); |
| if (core_clk_idx < 0) { |
| dev_err(dma->dev, "Can get clock index for dma %s\n", |
| MSM_JPEGDMA_CORE_CLK); |
| } |
| |
| ret = clk_set_rate(dma->clk[core_clk_idx], new_sp.core_clock); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail Core clock rate %d\n", ret); |
| return -EINVAL; |
| } |
| dma->active_clock_rate = speed->core_clock; |
| |
| dma->bus_vectors.ab = new_sp.bus_ab; |
| dma->bus_vectors.ib = new_sp.bus_ib; |
| |
| ret = msm_bus_scale_client_update_request(dma->bus_client, 0); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail bus scale update %d\n", ret); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_add_plane_offset - Add plane offset to all pipelines. |
| * @plane: Jpeg dma plane configuration. |
| * @in_offset: Input plane offset. |
| * @out_offset: Output plane offset. |
| */ |
| static int msm_jpegdma_hw_add_plane_offset(struct msm_jpegdma_plane *plane, |
| unsigned int in_offset, unsigned int out_offset) |
| { |
| int i; |
| |
| for (i = 0; i < plane->active_pipes; i++) { |
| plane->config[i].in_offset += in_offset; |
| plane->config[i].out_offset += out_offset; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_calc_config - Calculate plane configuration. |
| * @size_cfg: Size configuration. |
| * @plane: Plane configuration need to be calculated. |
| */ |
| static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, |
| struct msm_jpegdma_plane *plane) |
| { |
| u64 scale_hor, scale_ver, phase; |
| u64 in_width, in_height; |
| u64 out_width, out_height; |
| struct msm_jpegdma_config *config; |
| int i; |
| |
| if (!size_cfg->out_size.width || !size_cfg->out_size.height) |
| return -EINVAL; |
| |
| config = &plane->config[0]; |
| config->scale_cfg.enable = 0; |
| |
| in_width = size_cfg->in_size.width; |
| out_width = size_cfg->out_size.width; |
| scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI), |
| out_width); |
| if (scale_hor != MSM_JPEGDMA_SCALE_UNI) |
| config->scale_cfg.enable = 1; |
| |
| in_height = size_cfg->in_size.height; |
| out_height = size_cfg->out_size.height; |
| scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), |
| out_height); |
| if (scale_ver != MSM_JPEGDMA_SCALE_UNI) |
| config->scale_cfg.enable = 1; |
| |
| config->scale_cfg.ver_scale = scale_ver; |
| config->scale_cfg.hor_scale = scale_hor; |
| |
| for (i = 0; ARRAY_SIZE(msm_jpegdma_block_sel); i++) |
| if (scale_hor <= msm_jpegdma_block_sel[i].div) |
| break; |
| |
| if (i == ARRAY_SIZE(msm_jpegdma_block_sel)) |
| return -EINVAL; |
| |
| config->block_cfg.block = msm_jpegdma_block_sel[i]; |
| |
| if (plane->active_pipes > 1) { |
| phase = jpegdma_do_div((out_height * scale_ver + |
| (plane->active_pipes - 1)), plane->active_pipes); |
| phase &= (u64)(MSM_JPEGDMA_SCALE_UNI - 1); |
| out_height = jpegdma_do_div((out_height + |
| (plane->active_pipes - 1)), plane->active_pipes); |
| in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI; |
| } |
| |
| config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width, |
| config->block_cfg.block.width); |
| |
| config->block_cfg.blocks_per_col = out_height; |
| |
| config->block_cfg.h_step = config->block_cfg.block.width; |
| config->size_cfg.out_size.width = out_width; |
| config->block_cfg.h_step_last = (uint32_t) do_div(out_width, |
| config->block_cfg.block.width); |
| if (!config->block_cfg.h_step_last) |
| config->block_cfg.h_step_last = config->block_cfg.h_step; |
| else |
| config->block_cfg.blocks_per_row++; |
| |
| config->block_cfg.v_step = 1; |
| config->block_cfg.v_step_last = 1; |
| |
| config->size_cfg = *size_cfg; |
| config->size_cfg.in_size.width = in_width; |
| config->size_cfg.in_size.height = in_height; |
| config->size_cfg.out_size.height = out_height; |
| config->in_offset = 0; |
| config->out_offset = 0; |
| |
| if (plane->active_pipes > 1) { |
| plane->config[1] = *config; |
| /* Recalculate offset for second pipe */ |
| plane->config[1].in_offset = |
| config->size_cfg.in_size.scanline * |
| config->size_cfg.in_size.stride; |
| |
| plane->config[1].out_offset = |
| config->size_cfg.out_size.scanline * |
| config->size_cfg.out_size.stride; |
| |
| plane->config[1].phase = phase; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_check_config - Check configuration based on size is possible. |
| *@dma: Pointer to dma device. |
| * @size_cfg: Size configuration. |
| */ |
| int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size_config *size_cfg) |
| { |
| u64 in_width, in_height; |
| u64 out_width, out_height; |
| u64 scale; |
| |
| if (!size_cfg->out_size.width || !size_cfg->out_size.height) |
| return -EINVAL; |
| |
| in_width = size_cfg->in_size.width; |
| out_width = size_cfg->out_size.width; |
| scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)), |
| out_width); |
| if (scale < MSM_JPEGDMA_SCALE_UNI) |
| return -EINVAL; |
| |
| |
| in_height = size_cfg->in_size.height; |
| out_height = size_cfg->out_size.height; |
| scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), |
| out_height); |
| if (scale < MSM_JPEGDMA_SCALE_UNI) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_set_config - Set dma configuration based on size. |
| *@dma: Pointer to dma device. |
| * @size_cfg: Size configuration. |
| * @plane_cfg: Calculated plane configuration. |
| */ |
| int msm_jpegdma_hw_set_config(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_size_config *size_cfg, |
| struct msm_jpegdma_plane_config *plane_cfg) |
| { |
| unsigned int in_offset; |
| unsigned int out_offset; |
| struct msm_jpegdma_size_config plane_size; |
| int ret; |
| int i; |
| |
| if (!size_cfg->format.colplane_h || !size_cfg->format.colplane_v) |
| return -EINVAL; |
| |
| ret = msm_jpegdma_hw_calc_speed(dma, size_cfg, &plane_cfg->speed); |
| if (ret < 0) |
| return -EINVAL; |
| |
| dma->active_clock_rate = 0; |
| |
| plane_cfg->plane[0].active_pipes = dma->hw_num_pipes; |
| plane_cfg->plane[0].type = size_cfg->format.planes[0]; |
| msm_jpegdma_hw_calc_config(size_cfg, &plane_cfg->plane[0]); |
| |
| in_offset = size_cfg->in_offset; |
| out_offset = size_cfg->out_offset; |
| |
| msm_jpegdma_hw_add_plane_offset(&plane_cfg->plane[0], |
| in_offset, out_offset); |
| |
| if (size_cfg->format.num_planes == 1) |
| return 0; |
| |
| in_offset += (size_cfg->in_size.scanline * |
| size_cfg->in_size.stride); |
| out_offset += (size_cfg->out_size.scanline * |
| size_cfg->out_size.stride); |
| |
| memset(&plane_size, 0x00, sizeof(plane_size)); |
| for (i = 1; i < size_cfg->format.num_planes; i++) { |
| plane_cfg->plane[i].active_pipes = dma->hw_num_pipes; |
| plane_cfg->plane[i].type = size_cfg->format.planes[i]; |
| |
| if (size_cfg->in_size.top) |
| plane_size.in_size.top = size_cfg->in_size.top / |
| size_cfg->format.colplane_v; |
| |
| if (size_cfg->in_size.left) |
| plane_size.in_size.left = size_cfg->in_size.left / |
| size_cfg->format.colplane_h; |
| |
| plane_size.in_size.width = size_cfg->in_size.width / |
| size_cfg->format.colplane_h; |
| plane_size.in_size.height = size_cfg->in_size.height / |
| size_cfg->format.colplane_v; |
| plane_size.in_size.scanline = size_cfg->in_size.scanline / |
| size_cfg->format.colplane_v; |
| |
| plane_size.in_size.stride = size_cfg->in_size.stride; |
| |
| plane_size.out_size.width = size_cfg->out_size.width / |
| size_cfg->format.colplane_h; |
| plane_size.out_size.height = size_cfg->out_size.height / |
| size_cfg->format.colplane_v; |
| plane_size.out_size.scanline = size_cfg->out_size.scanline / |
| size_cfg->format.colplane_v; |
| |
| plane_size.out_size.stride = size_cfg->out_size.stride; |
| |
| plane_size.format = size_cfg->format; |
| plane_size.fps = size_cfg->fps; |
| |
| msm_jpegdma_hw_calc_config(&plane_size, |
| &plane_cfg->plane[i]); |
| |
| msm_jpegdma_hw_add_plane_offset(&plane_cfg->plane[i], |
| in_offset, out_offset); |
| |
| in_offset += (plane_size.in_size.scanline * |
| plane_size.in_size.stride); |
| out_offset += (plane_size.out_size.scanline * |
| plane_size.out_size.stride); |
| } |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_start - Start dma processing. |
| *@dma: Pointer to dma device. |
| * @addr: Input address. |
| * @plane: Plane configuration. |
| * @speed: Clock and bus bandwidth configuration. |
| */ |
| int msm_jpegdma_hw_start(struct msm_jpegdma_device *dma, |
| struct msm_jpegdma_addr *addr, |
| struct msm_jpegdma_plane *plane, |
| struct msm_jpegdma_speed *speed) |
| { |
| struct msm_jpegdma_config *cfg; |
| struct msm_jpegdma_addr prefetch_max_addr; |
| unsigned int prefetch_in_size; |
| unsigned int prefetch_out_size; |
| |
| int ret; |
| |
| if (!plane->active_pipes) |
| return -EINVAL; |
| |
| if (plane->active_pipes > MSM_JPEGDMA_MAX_PIPES) |
| return -EINVAL; |
| ret = msm_jpegdma_hw_set_speed(dma, &plane->config[0].size_cfg, speed); |
| if (ret < 0) |
| return -EINVAL; |
| |
| msm_jpegdma_hw_core_config(dma, plane->active_pipes, |
| plane->config[0].scale_cfg.enable, |
| plane->config[1].scale_cfg.enable); |
| |
| cfg = &plane->config[0]; |
| msm_jpegdma_hw_scale_0_config(dma, &cfg->scale_cfg); |
| |
| msm_jpegdma_hw_fe_0_block(dma, &cfg->block_cfg, plane->type); |
| msm_jpegdma_hw_fe_0_phase(dma, cfg->phase); |
| msm_jpegdma_hw_fe_0_size(dma, &cfg->size_cfg.in_size, plane->type); |
| msm_jpegdma_hw_fe_0_addr(dma, addr->in_addr + cfg->in_offset); |
| prefetch_in_size = cfg->size_cfg.in_size.stride * |
| cfg->size_cfg.in_size.scanline; |
| |
| msm_jpegdma_hw_we_0_block(dma, &cfg->block_cfg, plane->type); |
| msm_jpegdma_hw_we_0_size(dma, &cfg->size_cfg.out_size); |
| msm_jpegdma_hw_we_0_addr(dma, addr->out_addr + cfg->out_offset); |
| prefetch_out_size = cfg->size_cfg.out_size.stride * |
| cfg->size_cfg.out_size.scanline; |
| |
| if (plane->active_pipes > 1) { |
| cfg = &plane->config[1]; |
| msm_jpegdma_hw_scale_1_config(dma, &cfg->scale_cfg); |
| |
| msm_jpegdma_hw_fe_1_block(dma, &cfg->block_cfg, plane->type); |
| msm_jpegdma_hw_fe_1_phase(dma, cfg->phase); |
| msm_jpegdma_hw_fe_1_size(dma, &cfg->size_cfg.in_size, |
| plane->type); |
| msm_jpegdma_hw_fe_1_addr(dma, addr->in_addr + cfg->in_offset); |
| prefetch_in_size += (cfg->size_cfg.in_size.stride * |
| cfg->size_cfg.in_size.scanline); |
| |
| msm_jpegdma_hw_we_1_block(dma, &cfg->block_cfg, plane->type); |
| msm_jpegdma_hw_we_1_size(dma, &cfg->size_cfg.out_size); |
| msm_jpegdma_hw_we_1_addr(dma, addr->out_addr + cfg->out_offset); |
| prefetch_out_size += (cfg->size_cfg.out_size.stride * |
| cfg->size_cfg.out_size.scanline); |
| } |
| |
| if (prefetch_in_size > 0 && prefetch_out_size > 0) { |
| prefetch_max_addr.in_addr = addr->in_addr + |
| (prefetch_in_size - 1); |
| prefetch_max_addr.out_addr = addr->out_addr + |
| (prefetch_out_size - 1); |
| msm_jpegdma_hw_config_mmu_prefetch(dma, addr, |
| &prefetch_max_addr); |
| } |
| |
| msm_jpegdma_hw_run(dma); |
| |
| return 1; |
| } |
| |
| /* |
| * msm_jpegdma_hw_abort - abort dma processing. |
| *@dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_abort(struct msm_jpegdma_device *dma) |
| { |
| int ret; |
| |
| ret = msm_jpegdma_hw_halt(dma); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to halt hw\n"); |
| return ret; |
| } |
| |
| ret = msm_jpegdma_hw_reset(dma); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to reset hw\n"); |
| return ret; |
| } |
| return 0; |
| } |
| |
| /* |
| * msm_jpegdma_hw_irq - Dma irq handler. |
| * @irq: Irq number. |
| * @dev_id: Pointer to dma device. |
| */ |
| static irqreturn_t msm_jpegdma_hw_irq(int irq, void *dev_id) |
| { |
| struct msm_jpegdma_device *dma = dev_id; |
| |
| u32 irq_status; |
| |
| irq_status = msm_jpegdma_hw_get_irq_status(dma); |
| msm_jpegdma_hw_clear_irq(dma, irq_status); |
| |
| if (irq_status & MSM_JPEGDMA_IRQ_STATUS_RST_DONE) { |
| dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ reset done\n"); |
| complete_all(&dma->hw_reset_completion); |
| } |
| |
| if (irq_status & MSM_JPEGDMA_IRQ_STATUS_AXI_HALT) { |
| dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ AXI halt\n"); |
| complete_all(&dma->hw_halt_completion); |
| } |
| |
| if (irq_status & MSM_JPEGDMA_IRQ_STATUS_SESSION_DONE) { |
| dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ session done\n"); |
| msm_jpegdma_isr_processing_done(dma); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| /* |
| * msm_jpegdma_hw_request_irq - Request dma irq. |
| * @pdev: Pointer to platform device. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_request_irq(struct platform_device *pdev, |
| struct msm_jpegdma_device *dma) |
| { |
| int ret; |
| |
| dma->irq_num = platform_get_irq(pdev, 0); |
| if (dma->irq_num < 0) { |
| dev_err(dma->dev, "Can not get dma core irq resource\n"); |
| ret = -ENODEV; |
| goto error_irq; |
| } |
| |
| ret = request_threaded_irq(dma->irq_num, NULL, |
| msm_jpegdma_hw_irq, IRQF_ONESHOT | IRQF_TRIGGER_RISING, |
| dev_name(&pdev->dev), dma); |
| if (ret) { |
| dev_err(dma->dev, "Can not claim wrapper IRQ %d\n", |
| dma->irq_num); |
| goto error_irq; |
| } |
| |
| return 0; |
| |
| error_irq: |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_release_mem_resources - Releases memory resources. |
| * @dma: Pointer to dma device. |
| */ |
| void msm_jpegdma_hw_release_mem_resources(struct msm_jpegdma_device *dma) |
| { |
| int i, reserve_mem_flag; |
| char *dev_name; |
| |
| /* Prepare memory resources */ |
| for (i = 0; i < MSM_JPEGDMA_IOMEM_LAST; i++) { |
| |
| switch (i) { |
| case MSM_JPEGDMA_IOMEM_CORE: |
| dev_name = "jpeg_hw"; |
| reserve_mem_flag = true; |
| break; |
| case MSM_JPEGDMA_IOMEM_VBIF: |
| dev_name = "jpeg_vbif"; |
| reserve_mem_flag = false; |
| break; |
| default: |
| pr_err("%s: Invalid device : %d\n", __func__, i); |
| return; |
| } |
| /* release the device address */ |
| msm_camera_put_reg_base(dma->pdev, dma->iomem_base[i], dev_name, |
| reserve_mem_flag); |
| } |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_mem_resources - Get memory resources. |
| * @pdev: Pointer to dma platform device. |
| * @dma: Pointer to dma device. |
| * |
| * Get and ioremap platform memory resources. |
| */ |
| int msm_jpegdma_hw_get_mem_resources(struct platform_device *pdev, |
| struct msm_jpegdma_device *dma) |
| { |
| int i; |
| int ret = 0; |
| char *dev_name; |
| int reserve_mem_flag; |
| |
| /* Prepare memory resources */ |
| for (i = 0; i < MSM_JPEGDMA_IOMEM_LAST; i++) { |
| |
| switch (i) { |
| case MSM_JPEGDMA_IOMEM_CORE: |
| dev_name = "jpeg_hw"; |
| reserve_mem_flag = true; |
| break; |
| case MSM_JPEGDMA_IOMEM_VBIF: |
| dev_name = "jpeg_vbif"; |
| reserve_mem_flag = false; |
| break; |
| default: |
| pr_err("%s: Invalid device : %d\n", __func__, i); |
| return -EINVAL; |
| } |
| /* get the device address base */ |
| dma->iomem_base[i] = |
| msm_camera_get_reg_base(pdev, dev_name, |
| reserve_mem_flag); |
| if (!dma->iomem_base[i]) { |
| dev_err(dma->dev, "%s can not remap region\n", |
| dev_name); |
| ret = -ENODEV; |
| break; |
| } |
| } |
| |
| if (ret < 0) |
| msm_jpegdma_hw_release_mem_resources(dma); |
| |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_max_downscale - Get max downscale factor from dtsi. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_get_max_downscale(struct msm_jpegdma_device *dma) |
| { |
| int ret; |
| int max_ds_factor; |
| |
| ret = of_property_read_u32(dma->dev->of_node, |
| "qcom,max-ds-factor", &max_ds_factor); |
| if (ret < 0) { |
| dev_err(dma->dev, "cannot read qcom,max-ds-factor from dtsi\n"); |
| return ret; |
| } |
| dev_dbg(dma->dev, "max_ds_factor is %d\n", max_ds_factor); |
| return max_ds_factor; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_qos - Get dma qos settings from device-tree. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_get_qos(struct msm_jpegdma_device *dma) |
| { |
| int i, j; |
| int ret; |
| unsigned int cnt; |
| const void *property; |
| |
| property = of_get_property(dma->dev->of_node, |
| "qcom,qos-reg-settings", &cnt); |
| if (!property || !cnt) { |
| dev_dbg(dma->dev, "Missing qos settings\n"); |
| return 0; |
| } |
| |
| cnt /= 4; |
| if (cnt % 2) |
| return -EINVAL; |
| |
| dma->qos_regs_num = cnt / 2; |
| |
| dma->qos_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) * |
| dma->qos_regs_num), GFP_KERNEL); |
| if (!dma->qos_regs) |
| return -ENOMEM; |
| |
| for (i = 0, j = 0; i < cnt; i += 2, j++) { |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,qos-reg-settings", i, |
| &dma->qos_regs[j].reg); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read qos reg %d\n", j); |
| goto error; |
| } |
| |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,qos-reg-settings", i + 1, |
| &dma->qos_regs[j].val); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read qos setting %d\n", j); |
| goto error; |
| } |
| dev_dbg(dma->dev, "Qos idx %d, reg %x val %x\n", j, |
| dma->qos_regs[j].reg, dma->qos_regs[j].val); |
| } |
| |
| return 0; |
| error: |
| kfree(dma->qos_regs); |
| dma->qos_regs = NULL; |
| |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_put_qos - Free dma qos settings. |
| * @dma: Pointer to dma device. |
| */ |
| void msm_jpegdma_hw_put_qos(struct msm_jpegdma_device *dma) |
| { |
| kfree(dma->qos_regs); |
| dma->qos_regs = NULL; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_vbif - Get dma vbif settings from device-tree. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_get_vbif(struct msm_jpegdma_device *dma) |
| { |
| int i, j; |
| int ret; |
| unsigned int cnt; |
| const void *property; |
| |
| property = of_get_property(dma->dev->of_node, "qcom,vbif-reg-settings", |
| &cnt); |
| if (!property || !cnt) { |
| dev_dbg(dma->dev, "Missing vbif settings\n"); |
| return 0; |
| } |
| |
| cnt /= 4; |
| if (cnt % 2) |
| return -EINVAL; |
| |
| dma->vbif_regs_num = cnt / 2; |
| |
| dma->vbif_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) * |
| dma->vbif_regs_num), GFP_KERNEL); |
| if (!dma->vbif_regs) |
| return -ENOMEM; |
| |
| for (i = 0, j = 0; i < cnt; i += 2, j++) { |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,vbif-reg-settings", i, |
| &dma->vbif_regs[j].reg); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read vbif reg %d\n", j); |
| goto error; |
| } |
| |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,vbif-reg-settings", i + 1, |
| &dma->vbif_regs[j].val); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read vbif setting %d\n", j); |
| goto error; |
| } |
| |
| dev_dbg(dma->dev, "Vbif idx %d, reg %x val %x\n", j, |
| dma->vbif_regs[j].reg, dma->vbif_regs[j].val); |
| } |
| |
| return 0; |
| error: |
| kfree(dma->vbif_regs); |
| dma->vbif_regs = NULL; |
| |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_put_vbif - Put dma clocks. |
| * @dma: Pointer to dma device. |
| */ |
| void msm_jpegdma_hw_put_vbif(struct msm_jpegdma_device *dma) |
| { |
| kfree(dma->vbif_regs); |
| dma->vbif_regs = NULL; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_prefetch - Get dma prefetch settings from device-tree. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_get_prefetch(struct msm_jpegdma_device *dma) |
| { |
| int i, j; |
| int ret; |
| unsigned int cnt; |
| const void *property; |
| |
| property = of_get_property(dma->dev->of_node, |
| "qcom,prefetch-reg-settings", &cnt); |
| if (!property || !cnt) { |
| dev_dbg(dma->dev, "Missing prefetch settings\n"); |
| return 0; |
| } |
| |
| cnt /= 4; |
| if (cnt % 2) |
| return -EINVAL; |
| |
| dma->prefetch_regs_num = cnt / 2; |
| |
| dma->prefetch_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) * |
| dma->prefetch_regs_num), GFP_KERNEL); |
| if (!dma->prefetch_regs) |
| return -ENOMEM; |
| |
| for (i = 0, j = 0; i < cnt; i += 2, j++) { |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,prefetch-reg-settings", i, |
| &dma->prefetch_regs[j].reg); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read prefetch reg %d\n", j); |
| goto error; |
| } |
| |
| ret = of_property_read_u32_index(dma->dev->of_node, |
| "qcom,prefetch-reg-settings", i + 1, |
| &dma->prefetch_regs[j].val); |
| if (ret < 0) { |
| dev_err(dma->dev, "can not read prefetch setting %d\n", |
| j); |
| goto error; |
| } |
| |
| dev_dbg(dma->dev, "Prefetch idx %d, reg %x val %x\n", j, |
| dma->prefetch_regs[j].reg, dma->prefetch_regs[j].val); |
| } |
| |
| return 0; |
| error: |
| kfree(dma->prefetch_regs); |
| dma->prefetch_regs = NULL; |
| |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_put_prefetch - free prefetch settings. |
| * @dma: Pointer to dma device. |
| */ |
| void msm_jpegdma_hw_put_prefetch(struct msm_jpegdma_device *dma) |
| { |
| kfree(dma->prefetch_regs); |
| dma->prefetch_regs = NULL; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get_capabilities - Get dma hw for performing any hw operation. |
| * @dma: Pointer to dma device. |
| */ |
| int msm_jpegdma_hw_get_capabilities(struct msm_jpegdma_device *dma) |
| { |
| int ret = 0; |
| |
| mutex_lock(&dma->lock); |
| |
| /* enable all the regulators */ |
| ret = msm_camera_regulator_enable(dma->dma_vdd, |
| dma->num_reg, true); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to enable regulators\n"); |
| goto error_regulators_get; |
| } |
| |
| /* enable all the clocks */ |
| ret = msm_camera_clk_enable(&dma->pdev->dev, |
| dma->jpeg_clk_info, dma->clk, |
| dma->num_clk, true); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to enable clocks\n"); |
| goto error_clocks; |
| } |
| |
| dma->hw_num_pipes = msm_jpegdma_hw_get_num_pipes(dma); |
| |
| /* disable all the clocks */ |
| msm_camera_clk_enable(&dma->pdev->dev, |
| dma->jpeg_clk_info, dma->clk, |
| dma->num_clk, false); |
| /* disable all the regulators */ |
| msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false); |
| |
| mutex_unlock(&dma->lock); |
| |
| return 0; |
| |
| error_clocks: |
| msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false); |
| error_regulators_get: |
| mutex_unlock(&dma->lock); |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_get - Get dma hw for performing any hw operation. |
| * @dma: Pointer to dma device. |
| * @clock_rate_idx: Clock rate index. |
| * |
| * Prepare dma hw for operation. Have reference count protected by |
| * dma device mutex. |
| */ |
| int msm_jpegdma_hw_get(struct msm_jpegdma_device *dma) |
| { |
| int ret; |
| |
| mutex_lock(&dma->lock); |
| if (dma->ref_count == 0) { |
| |
| dev_dbg(dma->dev, "msm_jpegdma_hw_get E\n"); |
| /* enable all the regulators */ |
| ret = msm_camera_regulator_enable(dma->dma_vdd, |
| dma->num_reg, true); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to enable regulators\n"); |
| goto error_regulators_get; |
| } |
| |
| /* enable all the clocks */ |
| ret = msm_camera_clk_enable(&dma->pdev->dev, |
| dma->jpeg_clk_info, dma->clk, |
| dma->num_clk, true); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to enable clocks\n"); |
| goto error_clocks; |
| } |
| |
| /* update the bus vector with valid bw */ |
| msm_camera_update_bus_vector(dma->bus_client, 1); |
| msm_jpegdma_hw_config_qos(dma); |
| msm_jpegdma_hw_config_vbif(dma); |
| |
| msm_camera_register_threaded_irq(dma->pdev, dma->irq, NULL, |
| msm_jpegdma_hw_irq, IRQF_ONESHOT | IRQF_TRIGGER_RISING, |
| dev_name(&dma->pdev->dev), dma); |
| msm_jpegdma_hw_enable_irq(dma); |
| |
| ret = msm_jpegdma_hw_reset(dma); |
| if (ret < 0) { |
| dev_err(dma->dev, "Fail to reset hw\n"); |
| goto error_hw_reset; |
| } |
| msm_jpegdma_hw_config_qos(dma); |
| msm_jpegdma_hw_config_mmu_prefetch(dma, NULL, NULL); |
| msm_jpegdma_hw_enable_irq(dma); |
| } |
| dma->ref_count++; |
| dev_dbg(dma->dev, "msm_jpegdma_hw_get X\n"); |
| mutex_unlock(&dma->lock); |
| |
| return 0; |
| |
| error_hw_reset: |
| msm_jpegdma_hw_disable_irq(dma); |
| msm_camera_clk_enable(&dma->pdev->dev, dma->jpeg_clk_info, |
| dma->clk, dma->num_clk, false); |
| error_clocks: |
| msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false); |
| error_regulators_get: |
| mutex_unlock(&dma->lock); |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_put - Put dma hw. |
| * @dma: Pointer to dma device. |
| * |
| * Release dma hw. Have reference count protected by |
| * dma device mutex. |
| */ |
| void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma) |
| { |
| mutex_lock(&dma->lock); |
| |
| if (WARN_ON(!dma->ref_count)) |
| goto err; |
| |
| if (--dma->ref_count == 0) { |
| msm_jpegdma_hw_halt(dma); |
| msm_jpegdma_hw_disable_irq(dma); |
| /* release the irq */ |
| msm_camera_unregister_irq(dma->pdev, |
| dma->irq, dma); |
| /* update the bus vector with zeroth vector */ |
| msm_camera_update_bus_vector(dma->bus_client, 0); |
| /* disable all the clocks */ |
| msm_camera_clk_enable(&dma->pdev->dev, dma->jpeg_clk_info, |
| dma->clk, dma->num_clk, false); |
| /* disable all the regulators */ |
| msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false); |
| } |
| /* Reset clock rate, need to be updated on next processing */ |
| dma->active_clock_rate = -1; |
| err: |
| mutex_unlock(&dma->lock); |
| } |
| |
| /* |
| * msm_jpegdma_hw_attach_iommu - Attach iommu to jpeg dma engine. |
| * @dma: Pointer to dma device. |
| * |
| * Iommu attach have reference count protected by |
| * dma device mutex. |
| */ |
| static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma) |
| { |
| int ret = -EINVAL; |
| |
| mutex_lock(&dma->lock); |
| |
| if (dma->iommu_attached_cnt == UINT_MAX) { |
| dev_err(dma->dev, "Max count reached! can not attach iommu\n"); |
| goto error; |
| } |
| |
| if (dma->iommu_attached_cnt == 0) { |
| ret = cam_smmu_get_handle(MSM_JPEGDMA_SMMU_NAME, |
| &dma->iommu_hndl); |
| if (ret < 0) { |
| dev_err(dma->dev, "Smmu get handle failed\n"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| ret = cam_smmu_ops(dma->iommu_hndl, CAM_SMMU_ATTACH); |
| if (ret < 0) { |
| dev_err(dma->dev, "Can not attach smmu.\n"); |
| goto error_attach; |
| } |
| } |
| dma->iommu_attached_cnt++; |
| mutex_unlock(&dma->lock); |
| |
| return 0; |
| error_attach: |
| cam_smmu_destroy_handle(dma->iommu_hndl); |
| error: |
| mutex_unlock(&dma->lock); |
| return ret; |
| } |
| |
| /* |
| * msm_jpegdma_hw_detach_iommu - Detach iommu from jpeg dma engine. |
| * @dma: Pointer to dma device. |
| * |
| * Iommu detach have reference count protected by |
| * dma device mutex. |
| */ |
| static void msm_jpegdma_hw_detach_iommu(struct msm_jpegdma_device *dma) |
| { |
| mutex_lock(&dma->lock); |
| |
| if (dma->iommu_attached_cnt == 0) { |
| dev_err(dma->dev, "There is no attached device\n"); |
| mutex_unlock(&dma->lock); |
| return; |
| } |
| |
| if (--dma->iommu_attached_cnt == 0) { |
| cam_smmu_ops(dma->iommu_hndl, CAM_SMMU_DETACH); |
| cam_smmu_destroy_handle(dma->iommu_hndl); |
| } |
| |
| mutex_unlock(&dma->lock); |
| } |
| |
| /* |
| * msm_jpegdma_hw_map_buffer - Map buffer to dma hw mmu. |
| * @dma: Pointer to dma device. |
| * @fd: Ion fd. |
| * @buf: dma buffer handle, for storing mapped buffer information. |
| * |
| * It will map ion fd to dma hw smmu. |
| */ |
| int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd, |
| struct msm_jpegdma_buf_handle *buf) |
| { |
| int ret; |
| |
| if (!dma || fd < 0) |
| return -EINVAL; |
| |
| ret = msm_jpegdma_hw_attach_iommu(dma); |
| if (ret < 0) |
| goto error; |
| |
| buf->dma = dma; |
| buf->fd = fd; |
| |
| ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd, |
| CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size); |
| if (ret < 0) { |
| dev_err(dma->dev, "Can not get physical address\n"); |
| goto error_get_phy; |
| } |
| |
| return buf->size; |
| |
| error_get_phy: |
| msm_jpegdma_hw_detach_iommu(dma); |
| error: |
| return -ENOMEM; |
| } |
| |
| /* |
| * msm_jpegdma_hw_unmap_buffer - Unmap buffer from dma hw mmu. |
| * @buf: dma buffer handle, for storing mapped buffer information. |
| */ |
| void msm_jpegdma_hw_unmap_buffer(struct msm_jpegdma_buf_handle *buf) |
| { |
| if (buf->size && buf->dma) { |
| cam_smmu_put_phy_addr(buf->dma->iommu_hndl, |
| buf->fd); |
| msm_jpegdma_hw_detach_iommu(buf->dma); |
| buf->size = 0; |
| } |
| buf->fd = -1; |
| buf->dma = NULL; |
| } |