mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 13:27:06 +09:00
Merge pull request #274 from OtherCrashOverride/xu4-mfc-next
Xu4 mfc next
This commit is contained in:
@@ -12,6 +12,7 @@ Required properties:
|
||||
(b) "samsung,mfc-v6" for MFC v6 present in Exynos5 SoCs
|
||||
(c) "samsung,mfc-v7" for MFC v7 present in Exynos5420 SoC
|
||||
(d) "samsung,mfc-v8" for MFC v8 present in Exynos5800 SoC
|
||||
(e) "samsung,exynos5433-mfc" for MFC v8 present in Exynos5433 SoC
|
||||
|
||||
- reg : Physical base address of the IP registers and length of memory
|
||||
mapped region.
|
||||
|
||||
@@ -386,7 +386,8 @@
|
||||
((w) * 144 + 8192 * (h) + 49216 + 1048576)
|
||||
#define S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(w, h) \
|
||||
(2096 * ((w) + (h) + 1))
|
||||
#define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h) ((w) * 400)
|
||||
#define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h) \
|
||||
S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h)
|
||||
#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6(w, h) \
|
||||
((w) * 32 + (h) * 128 + (((w) + 1) / 2) * 64 + 2112)
|
||||
#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6(w, h) \
|
||||
|
||||
@@ -393,6 +393,9 @@
|
||||
#define S5P_FIMV_REG_CLEAR_COUNT 0
|
||||
|
||||
/* Error handling defines */
|
||||
#define S5P_FIMV_ERR_NO_VALID_SEQ_HDR 67
|
||||
#define S5P_FIMV_ERR_INCOMPLETE_FRAME 124
|
||||
#define S5P_FIMV_ERR_TIMEOUT 140
|
||||
#define S5P_FIMV_ERR_WARNINGS_START 145
|
||||
#define S5P_FIMV_ERR_DEC_MASK 0xFFFF
|
||||
#define S5P_FIMV_ERR_DEC_SHIFT 0
|
||||
|
||||
@@ -641,8 +641,11 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
|
||||
case S5P_MFC_R2H_CMD_ERR_RET:
|
||||
/* An error has occurred */
|
||||
if (ctx->state == MFCINST_RUNNING &&
|
||||
s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=
|
||||
dev->warn_start)
|
||||
(s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=
|
||||
dev->warn_start ||
|
||||
err == S5P_FIMV_ERR_NO_VALID_SEQ_HDR ||
|
||||
err == S5P_FIMV_ERR_INCOMPLETE_FRAME ||
|
||||
err == S5P_FIMV_ERR_TIMEOUT))
|
||||
s5p_mfc_handle_frame(ctx, reason, err);
|
||||
else
|
||||
s5p_mfc_handle_error(dev, ctx, reason, err);
|
||||
@@ -848,6 +851,11 @@ static int s5p_mfc_open(struct file *file)
|
||||
ret = -ENOENT;
|
||||
goto err_queue_init;
|
||||
}
|
||||
/*
|
||||
* We'll do mostly sequential access, so sacrifice TLB efficiency for
|
||||
* faster allocation.
|
||||
*/
|
||||
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
|
||||
q->mem_ops = &vb2_dma_contig_memops;
|
||||
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
||||
ret = vb2_queue_init(q);
|
||||
@@ -878,6 +886,12 @@ static int s5p_mfc_open(struct file *file)
|
||||
* will keep the value of bytesused intact.
|
||||
*/
|
||||
q->allow_zero_bytesused = 1;
|
||||
|
||||
/*
|
||||
* We'll do mostly sequential access, so sacrifice TLB efficiency for
|
||||
* faster allocation.
|
||||
*/
|
||||
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
|
||||
q->mem_ops = &vb2_dma_contig_memops;
|
||||
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
||||
ret = vb2_queue_init(q);
|
||||
@@ -949,11 +963,13 @@ static int s5p_mfc_release(struct file *file)
|
||||
mfc_debug(2, "Last instance\n");
|
||||
s5p_mfc_deinit_hw(dev);
|
||||
del_timer_sync(&dev->watchdog_timer);
|
||||
s5p_mfc_clock_off();
|
||||
if (s5p_mfc_power_off() < 0)
|
||||
mfc_err("Power off failed\n");
|
||||
} else {
|
||||
mfc_debug(2, "Shutting down clock\n");
|
||||
s5p_mfc_clock_off();
|
||||
}
|
||||
mfc_debug(2, "Shutting down clock\n");
|
||||
s5p_mfc_clock_off();
|
||||
}
|
||||
if (dev)
|
||||
dev->ctx[ctx->num] = NULL;
|
||||
@@ -1389,31 +1405,9 @@ static int s5p_mfc_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s5p_mfc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
|
||||
|
||||
atomic_set(&m_dev->pm.power, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_mfc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
|
||||
|
||||
atomic_set(&m_dev->pm.power, 1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Power management */
|
||||
static const struct dev_pm_ops s5p_mfc_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
|
||||
SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
|
||||
@@ -1440,6 +1434,9 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = {
|
||||
.buf_size = &buf_size_v5,
|
||||
.buf_align = &mfc_buf_align_v5,
|
||||
.fw_name[0] = "s5p-mfc.fw",
|
||||
.clk_names = {"mfc", "sclk_mfc"},
|
||||
.num_clocks = 2,
|
||||
.use_clock_gating = true,
|
||||
};
|
||||
|
||||
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
|
||||
@@ -1472,6 +1469,8 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = {
|
||||
* for init buffer command
|
||||
*/
|
||||
.fw_name[1] = "s5p-mfc-v6-v2.fw",
|
||||
.clk_names = {"mfc"},
|
||||
.num_clocks = 1,
|
||||
};
|
||||
|
||||
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
|
||||
@@ -1499,6 +1498,8 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = {
|
||||
.buf_size = &buf_size_v7,
|
||||
.buf_align = &mfc_buf_align_v7,
|
||||
.fw_name[0] = "s5p-mfc-v7.fw",
|
||||
.clk_names = {"mfc", "sclk_mfc"},
|
||||
.num_clocks = 2,
|
||||
};
|
||||
|
||||
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
|
||||
@@ -1526,6 +1527,19 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = {
|
||||
.buf_size = &buf_size_v8,
|
||||
.buf_align = &mfc_buf_align_v8,
|
||||
.fw_name[0] = "s5p-mfc-v8.fw",
|
||||
.clk_names = {"mfc"},
|
||||
.num_clocks = 1,
|
||||
};
|
||||
|
||||
static struct s5p_mfc_variant mfc_drvdata_v8_5433 = {
|
||||
.version = MFC_VERSION_V8,
|
||||
.version_bit = MFC_V8_BIT,
|
||||
.port_num = MFC_NUM_PORTS_V8,
|
||||
.buf_size = &buf_size_v8,
|
||||
.buf_align = &mfc_buf_align_v8,
|
||||
.fw_name[0] = "s5p-mfc-v8.fw",
|
||||
.clk_names = {"pclk", "aclk", "aclk_xiu"},
|
||||
.num_clocks = 3,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_mfc_match[] = {
|
||||
@@ -1541,6 +1555,9 @@ static const struct of_device_id exynos_mfc_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,mfc-v8",
|
||||
.data = &mfc_drvdata_v8,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-mfc",
|
||||
.data = &mfc_drvdata_v8_5433,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -104,6 +104,8 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
|
||||
#define S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET 16
|
||||
#define S5P_MFC_R2H_CMD_ERR_RET 32
|
||||
|
||||
#define MFC_MAX_CLOCKS 4
|
||||
|
||||
#define mfc_read(dev, offset) readl(dev->regs_base + (offset))
|
||||
#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
|
||||
(offset))
|
||||
@@ -197,9 +199,12 @@ struct s5p_mfc_buf {
|
||||
* struct s5p_mfc_pm - power management data structure
|
||||
*/
|
||||
struct s5p_mfc_pm {
|
||||
struct clk *clock;
|
||||
struct clk *clock_gate;
|
||||
atomic_t power;
|
||||
const char **clk_names;
|
||||
struct clk *clocks[MFC_MAX_CLOCKS];
|
||||
int num_clocks;
|
||||
bool use_clock_gating;
|
||||
|
||||
struct device *device;
|
||||
};
|
||||
|
||||
@@ -235,6 +240,9 @@ struct s5p_mfc_variant {
|
||||
struct s5p_mfc_buf_size *buf_size;
|
||||
struct s5p_mfc_buf_align *buf_align;
|
||||
char *fw_name[MFC_FW_MAX_VERSIONS];
|
||||
const char *clk_names[MFC_MAX_CLOCKS];
|
||||
int num_clocks;
|
||||
bool use_clock_gating;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,12 @@ extern int mfc_debug_level;
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define mfc_err_limited(fmt, args...) \
|
||||
do { \
|
||||
printk_ratelimited(KERN_ERR "%s:%d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define mfc_info(fmt, args...) \
|
||||
do { \
|
||||
printk(KERN_INFO "%s:%d: " fmt, \
|
||||
|
||||
@@ -642,7 +642,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
||||
int ret;
|
||||
|
||||
if (ctx->state == MFCINST_ERROR) {
|
||||
mfc_err("Call on DQBUF after unrecoverable error\n");
|
||||
mfc_err_limited("Call on DQBUF after unrecoverable error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -793,18 +793,17 @@ static int vidioc_g_crop(struct file *file, void *priv,
|
||||
cr->c.top = top;
|
||||
cr->c.width = ctx->img_width - left - right;
|
||||
cr->c.height = ctx->img_height - top - bottom;
|
||||
mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
|
||||
"w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
|
||||
cr->c.width, cr->c.height, right, bottom,
|
||||
ctx->buf_width, ctx->buf_height);
|
||||
mfc_debug(2, "Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d b=%d fw=%d fh=%d\n",
|
||||
left, top, cr->c.width, cr->c.height, right, bottom,
|
||||
ctx->buf_width, ctx->buf_height);
|
||||
} else {
|
||||
cr->c.left = 0;
|
||||
cr->c.top = 0;
|
||||
cr->c.width = ctx->img_width;
|
||||
cr->c.height = ctx->img_height;
|
||||
mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
|
||||
"fh=%d\n", cr->c.width, cr->c.height, ctx->buf_width,
|
||||
ctx->buf_height);
|
||||
mfc_debug(2, "Cropping info: w=%d h=%d fw=%d fh=%d\n",
|
||||
cr->c.width, cr->c.height, ctx->buf_width,
|
||||
ctx->buf_height);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1268,7 +1268,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
||||
int ret;
|
||||
|
||||
if (ctx->state == MFCINST_ERROR) {
|
||||
mfc_err("Call on DQBUF after unrecoverable error\n");
|
||||
mfc_err_limited("Call on DQBUF after unrecoverable error\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
||||
|
||||
@@ -45,13 +45,13 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base,
|
||||
b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
|
||||
|
||||
if (!b->virt) {
|
||||
mfc_err("Allocating private buffer failed\n");
|
||||
mfc_err("Allocating private buffer of size %zu failed\n",
|
||||
b->size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (b->dma < base) {
|
||||
mfc_err("Invaling memory configuration!\n");
|
||||
mfc_err("Allocated buffer (%pad) is lower than memory base address (%pad)\n",
|
||||
mfc_err("Invalid memory configuration - buffer (%pad) is below base memory address(%pad)\n",
|
||||
&b->dma, &base);
|
||||
dma_free_coherent(dev, b->size, b->virt, b->dma);
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1293,14 +1293,11 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
|
||||
* First set the output frame buffers
|
||||
*/
|
||||
if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
|
||||
mfc_err("It seems that not all destionation buffers were "
|
||||
"mmaped\nMFC requires that all destination are mmaped "
|
||||
"before starting processing\n");
|
||||
mfc_err("It seems that not all destionation buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (list_empty(&ctx->src_queue)) {
|
||||
mfc_err("Header has been deallocated in the middle of"
|
||||
" initialization\n");
|
||||
mfc_err("Header has been deallocated in the middle of initialization\n");
|
||||
return -EIO;
|
||||
}
|
||||
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
|
||||
|
||||
@@ -18,129 +18,101 @@
|
||||
#include "s5p_mfc_debug.h"
|
||||
#include "s5p_mfc_pm.h"
|
||||
|
||||
#define MFC_GATE_CLK_NAME "mfc"
|
||||
#define MFC_SCLK_NAME "sclk_mfc"
|
||||
#define MFC_SCLK_RATE (200 * 1000000)
|
||||
|
||||
#define CLK_DEBUG
|
||||
|
||||
static struct s5p_mfc_pm *pm;
|
||||
static struct s5p_mfc_dev *p_dev;
|
||||
|
||||
#ifdef CLK_DEBUG
|
||||
static atomic_t clk_ref;
|
||||
#endif
|
||||
|
||||
int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
pm = &dev->pm;
|
||||
p_dev = dev;
|
||||
pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
|
||||
if (IS_ERR(pm->clock_gate)) {
|
||||
mfc_err("Failed to get clock-gating control\n");
|
||||
ret = PTR_ERR(pm->clock_gate);
|
||||
goto err_g_ip_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare(pm->clock_gate);
|
||||
if (ret) {
|
||||
mfc_err("Failed to prepare clock-gating control\n");
|
||||
goto err_p_ip_clk;
|
||||
}
|
||||
pm->num_clocks = dev->variant->num_clocks;
|
||||
pm->clk_names = dev->variant->clk_names;
|
||||
pm->device = &dev->plat_dev->dev;
|
||||
pm->clock_gate = NULL;
|
||||
|
||||
if (dev->variant->version != MFC_VERSION_V6) {
|
||||
pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME);
|
||||
if (IS_ERR(pm->clock)) {
|
||||
mfc_info("Failed to get MFC special clock control\n");
|
||||
pm->clock = NULL;
|
||||
} else {
|
||||
clk_set_rate(pm->clock, MFC_SCLK_RATE);
|
||||
ret = clk_prepare_enable(pm->clock);
|
||||
if (ret) {
|
||||
mfc_err("Failed to enable MFC special clock\n");
|
||||
goto err_s_clk;
|
||||
}
|
||||
/* clock control */
|
||||
for (i = 0; i < pm->num_clocks; i++) {
|
||||
pm->clocks[i] = devm_clk_get(pm->device, pm->clk_names[i]);
|
||||
if (IS_ERR(pm->clocks[i])) {
|
||||
mfc_err("Failed to get clock: %s\n",
|
||||
pm->clk_names[i]);
|
||||
return PTR_ERR(pm->clocks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
atomic_set(&pm->power, 0);
|
||||
#ifdef CONFIG_PM
|
||||
pm->device = &dev->plat_dev->dev;
|
||||
pm_runtime_enable(pm->device);
|
||||
#endif
|
||||
#ifdef CLK_DEBUG
|
||||
atomic_set(&clk_ref, 0);
|
||||
#endif
|
||||
return 0;
|
||||
if (dev->variant->use_clock_gating)
|
||||
pm->clock_gate = pm->clocks[0];
|
||||
|
||||
err_s_clk:
|
||||
clk_put(pm->clock);
|
||||
pm->clock = NULL;
|
||||
err_p_ip_clk:
|
||||
clk_put(pm->clock_gate);
|
||||
pm->clock_gate = NULL;
|
||||
err_g_ip_clk:
|
||||
return ret;
|
||||
pm_runtime_enable(pm->device);
|
||||
atomic_set(&clk_ref, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
|
||||
{
|
||||
if (dev->variant->version != MFC_VERSION_V6 &&
|
||||
pm->clock) {
|
||||
clk_disable_unprepare(pm->clock);
|
||||
clk_put(pm->clock);
|
||||
pm->clock = NULL;
|
||||
}
|
||||
clk_unprepare(pm->clock_gate);
|
||||
clk_put(pm->clock_gate);
|
||||
pm->clock_gate = NULL;
|
||||
#ifdef CONFIG_PM
|
||||
pm_runtime_disable(pm->device);
|
||||
#endif
|
||||
}
|
||||
|
||||
int s5p_mfc_clock_on(void)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef CLK_DEBUG
|
||||
atomic_inc(&clk_ref);
|
||||
mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
|
||||
#endif
|
||||
if (!IS_ERR_OR_NULL(pm->clock_gate))
|
||||
ret = clk_enable(pm->clock_gate);
|
||||
return ret;
|
||||
|
||||
return clk_enable(pm->clock_gate);
|
||||
}
|
||||
|
||||
void s5p_mfc_clock_off(void)
|
||||
{
|
||||
#ifdef CLK_DEBUG
|
||||
atomic_dec(&clk_ref);
|
||||
mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
|
||||
#endif
|
||||
if (!IS_ERR_OR_NULL(pm->clock_gate))
|
||||
clk_disable(pm->clock_gate);
|
||||
|
||||
clk_disable(pm->clock_gate);
|
||||
}
|
||||
|
||||
int s5p_mfc_power_on(void)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
return pm_runtime_get_sync(pm->device);
|
||||
#else
|
||||
atomic_set(&pm->power, 1);
|
||||
int i, ret = 0;
|
||||
|
||||
ret = pm_runtime_get_sync(pm->device);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* clock control */
|
||||
for (i = 0; i < pm->num_clocks; i++) {
|
||||
ret = clk_prepare_enable(pm->clocks[i]);
|
||||
if (ret < 0) {
|
||||
mfc_err("clock prepare failed for clock: %s\n",
|
||||
pm->clk_names[i]);
|
||||
i++;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* prepare for software clock gating */
|
||||
clk_disable(pm->clock_gate);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
err:
|
||||
while (--i > 0)
|
||||
clk_disable_unprepare(pm->clocks[i]);
|
||||
pm_runtime_put(pm->device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int s5p_mfc_power_off(void)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
int i;
|
||||
|
||||
/* finish software clock gating */
|
||||
clk_enable(pm->clock_gate);
|
||||
|
||||
for (i = 0; i < pm->num_clocks; i++)
|
||||
clk_disable_unprepare(pm->clocks[i]);
|
||||
|
||||
return pm_runtime_put_sync(pm->device);
|
||||
#else
|
||||
atomic_set(&pm->power, 0);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user