blob: 1416bd5850806f792fd88ff1c330dbb7ba0da980 [file] [log] [blame]
/* iva_vdma.c
*
* Copyright (C) 2016 Samsung Electronics Co.Ltd
* Authors:
* Ilkwan Kim <ilkwan.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/time.h>
#include "regs/iva_base_addr.h"
#include "regs/iva_vde_reg.h"
#include "iva_vdma.h"
#include "iva_pmu.h"
/* pixel/data types */
enum data_fmt_t {
e_data_8b = 0,
e_data_16b,
e_data_32b,
e_data_64b,
e_data_128b,
e_data_fmt_invalid = 255
};
enum vdma_ttype {
vdma_ttype_ext_to_loc = 0,
vdma_ttype_loc_to_ext = 1,
vdma_ttype_loc_to_loc = 2,
vdma_ttype_ext_to_ext = 3,
vdma_ttype_max
};
enum vdma_addr_mode {
vdma_addr_mode_2d = 0,
vdma_addr_mode_linear = 1,
vdma_addr_mode_max,
};
enum vdma_planar_mode {
vdma_planar = 0,
vdma_2_planes = 1,
vdma_3_planes = 2,
vdma_planar_max,
vdma_known = vdma_planar_max,/* not supported */
};
enum vdma_yuv_mode {
vdma_ym_yuyv = 0,
vdma_ym_yvyu = 1,
vdma_ym_uyvy = 2,
vdma_ym_vyuy = 3,
};
#define GET_DMA_BASE_F_ID(id) ((id == vdma_id_0) ? IVA_VDMA0_BASE_ADDR :\
IVA_VDMA1_BASE_ADDR)
#define GET_DMA_CH_BASE_F_HANDLE(hdl) (GET_DMA_BASE_F_ID(HALDLE_TO_ID(hdl)) +\
VDE_CH_BASE_ADDR(HALDLE_TO_CH(hdl)))
static void vdma_clear_planar_config(void *ch_base)
{
/* Planar mode of transfer */
writel(0x0, ch_base + VDE_CH_CFG_REG_YUV_ADDR);
/* clear the Planar buffer configurations not to use flow-control */
writel(0x0, ch_base + VDE_CH_P_CBWCFG_ADDR(0, 0));
writel(0x0, ch_base + VDE_CH_P_CBRCFG_ADDR(0, 0));
writel(0x0, ch_base + VDE_CH_P_CBWCFG_ADDR(1, 0));
writel(0x0, ch_base + VDE_CH_P_CBRCFG_ADDR(1, 0));
writel(0x0, ch_base + VDE_CH_P_CBWCFG_ADDR(2, 0));
writel(0x0, ch_base + VDE_CH_P_CBRCFG_ADDR(2, 0));
}
void iva_vdma_start_cmd(struct iva_dev_data *iva, uint32_t handle)
{
void *ch_base = iva->iva_va + GET_DMA_CH_BASE_F_HANDLE(handle);
/* start the DMA transfer really */
writel(VDE_CH_START_REG_TR_START_M,
ch_base + VDE_CH_START_REG_ADDR);
}
/* busy wait*/
void iva_vdma_wait_done(struct iva_dev_data *iva, uint32_t handle)
{
void *dma_base = iva->iva_va +
GET_DMA_BASE_F_ID(HALDLE_TO_ID(handle));
enum vdma_ch ch = HALDLE_TO_CH(handle);
uint32_t val;
do {
val = readl(dma_base + VDE_IRQ_RAW_STATUS_ADDR);
} while (!(val & (0x1 << ch))); // RSTAT_DONE high = done
/* clear */
writel(0x1 << ch, dma_base + VDE_IRQ_RAW_STATUS_ADDR);
}
int32_t iva_vdma_config_dram_to_mcu(struct iva_dev_data *iva,
uint32_t handle, uint32_t io_va,
uint32_t mcu_addr, uint32_t x_size, bool start)
{
uint32_t val;
void *ch_base = iva->iva_va + GET_DMA_CH_BASE_F_HANDLE(handle);
/* config for external source */
val = (vdma_addr_mode_linear << VDE_CH_CFG_REG_8_ADDR_MODE_S) |
(vdma_ttype_ext_to_ext << VDE_CH_CFG_REG_8_TTYPE_S) |
(e_data_8b << VDE_CH_CFG_REG_8_BPP_S) |
(x_size);
writel(val, ch_base + VDE_CH_CFG_REG_8_ADDR);
writel(0x0, ch_base + VDE_CH_CFG_REG_6_ADDR);
writel(0x0, ch_base + VDE_CH_CFG_REG_7_ADDR);
/* global source and destination address in DRAM */
writel(io_va, ch_base + VDE_CH_CFG_REG_0_ADDR);
writel(mcu_addr + IVA_VMCU_MEM_BASE_ADDR,
ch_base + VDE_CH_CFG_REG_2_ADDR);
/* BCI mode of transfer (used with vDSP TCM) */
writel(0x0, ch_base + VDE_CH_CFG_REG_BCI_ADDR);
/* clear to planar mode w/o flow-control */
vdma_clear_planar_config(ch_base);
/* clear the vCF circular buffer RP/WP configuration */
writel(0x0, ch_base + VDE_CH_CBRCFG_ADDR(0));
writel(0x0, ch_base + VDE_CH_CBWCFG_ADDR(0));
if (start)
iva_vdma_start_cmd(iva, handle);
return 0;
}
int32_t iva_vdma_enable(struct iva_dev_data *iva, enum vdma_id id, bool enable)
{
enum iva_pmu_ip_id ip;
switch (id) {
case vdma_id_0:
ip = pmu_vdma0_id;
break;
case vdma_id_1:
ip = pmu_vdma1_id;
break;
default:
dev_err(iva->dev, "%s() unknown vdma id(%d)\n", __func__, id);
return -EINVAL;
}
if (enable) {
iva_pmu_enable_clk(iva, ip, true);
iva_pmu_reset_hwa(iva, ip, false);
iva_pmu_reset_hwa(iva, ip, true);
} else {
iva_pmu_reset_hwa(iva, ip, false);
iva_pmu_enable_clk(iva, ip, false);
}
return 0;
}
#define IVA_VDMA_PRINT(dev, ch_base, reg) \
dev_info(dev, #reg "(0x%x)\n", readl(ch_base + reg))
void iva_vdma_show_status(struct iva_dev_data *iva)
{
struct device *dev = iva->dev;
uint32_t vdma_hd;
int id, ch;
void *ch_base;
uint32_t val;
bool vdma_clk;
for (id = vdma_id_0; id < vdma_id_max; id++) {
vdma_clk = !!iva_pmu_enable_clk(iva, pmu_vdma0_id << id, true);
for (ch = vdma_ch_0; ch < vdma_ch_max; ch++) {
vdma_hd = ID_CH_TO_HANDLE(id, ch);
ch_base = iva->iva_va + GET_DMA_CH_BASE_F_HANDLE(vdma_hd);
val = readl(ch_base + VDE_CH_STATUS_REG_0_ADDR);
if (!(val & VDE_CH_STATUS_0_CH_BUSY_M))
continue;
dev_info(dev, "--- iva vdma id(%d), ch(%d)---\n",
id, ch);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_STATUS_REG_0_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_0_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_2_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_4_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_5_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_6_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_7_ADDR);
IVA_VDMA_PRINT(dev, ch_base, VDE_CH_CFG_REG_8_ADDR);
}
if (!vdma_clk)
iva_pmu_enable_clk(iva, pmu_vdma0_id << id, false);
}
}