diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_rotator.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_rotator.c | 751 |
1 files changed, 363 insertions, 388 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 5bf1d6e..9e40790 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -15,13 +15,12 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/pm_runtime.h> -#include <linux/pm_qos_params.h> #include "drmP.h" #include "exynos_drm.h" #include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" +#include "exynos_drm_ipp.h" /* Configuration */ #define ROT_CONFIG 0x00 @@ -49,39 +48,31 @@ #define ROT_STATUS_IRQ_VAL_COMPLETE 1 #define ROT_STATUS_IRQ_VAL_ILLEGAL 2 -/* Sourc Buffer Address */ +/* Buffer Address */ #define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2)) +#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) -/* Source Buffer Size */ +/* Buffer Size */ #define ROT_SRC_BUF_SIZE 0x3c -#define ROT_SRC_BUF_SIZE_H(x) ((x) << 16) -#define ROT_SRC_BUF_SIZE_W(x) ((x) << 0) +#define ROT_DST_BUF_SIZE 0x5c +#define ROT_SET_BUF_SIZE_H(x) ((x) << 16) +#define ROT_SET_BUF_SIZE_W(x) ((x) << 0) +#define ROT_GET_BUF_SIZE_H(x) ((x) >> 16) +#define ROT_GET_BUF_SIZE_W(x) ((x) & 0xffff) -/* Source Crop Position */ +/* Crop Position */ #define ROT_SRC_CROP_POS 0x40 -#define ROT_SRC_CROP_POS_Y(x) ((x) << 16) -#define ROT_SRC_CROP_POS_X(x) ((x) << 0) +#define ROT_DST_CROP_POS 0x60 +#define ROT_CROP_POS_Y(x) ((x) << 16) +#define ROT_CROP_POS_X(x) ((x) << 0) /* Source Crop Size */ #define ROT_SRC_CROP_SIZE 0x44 #define ROT_SRC_CROP_SIZE_H(x) ((x) << 16) #define ROT_SRC_CROP_SIZE_W(x) ((x) << 0) -/* Destination Buffer Address */ -#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) - -/* Destination Buffer Size */ -#define ROT_DST_BUF_SIZE 0x5c -#define ROT_DST_BUF_SIZE_H(x) ((x) << 16) -#define ROT_DST_BUF_SIZE_W(x) ((x) << 0) - -/* Destination Crop Position */ -#define ROT_DST_CROP_POS 0x60 -#define ROT_DST_CROP_POS_Y(x) ((x) << 16) -#define ROT_DST_CROP_POS_X(x) ((x) << 0) - /* Round to nearest aligned value */ -#define ROT_ALIGN(x, align, mask) ((*(x) + (1 << ((align) - 1))) & (mask)) +#define ROT_ALIGN(x, align, mask) (((x) + (1 << ((align) - 1))) & (mask)) /* Minimum limit value */ #define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask)) /* Maximum limit value */ @@ -111,28 +102,11 @@ struct rot_context { struct resource *regs_res; void __iomem *regs; int irq; - int exec_ret; - struct exynos_drm_subdrv subdrv; - struct completion complete; - struct mutex exec_mutex; - spinlock_t irq_lock; - struct pm_qos_request_list pm_qos; + struct exynos_drm_ippdrv ippdrv; + int cur_buf_id[EXYNOS_DRM_OPS_MAX]; bool suspended; }; -struct rot_buffer { - dma_addr_t src_addr[DRM_EXYNOS_ROT_MAX_BUF]; - dma_addr_t dst_addr[DRM_EXYNOS_ROT_MAX_BUF]; - void *src_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - void *dst_gem_obj[DRM_EXYNOS_ROT_MAX_BUF]; - u32 src_cnt; - u32 dst_cnt; - u32 src_w; - u32 src_h; - u32 dst_w; - u32 dst_h; -}; - static void rotator_reg_set_irq(struct rot_context *rot, bool enable) { u32 value = readl(rot->regs + ROT_CONFIG); @@ -145,6 +119,14 @@ static void rotator_reg_set_irq(struct rot_context *rot, bool enable) writel(value, rot->regs + ROT_CONFIG); } +static u32 rotator_reg_get_format(struct rot_context *rot) +{ + u32 value = readl(rot->regs + ROT_CONTROL); + value &= ROT_CONTROL_FMT_MASK; + + return value; +} + static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) { u32 value = readl(rot->regs + ROT_CONTROL); @@ -155,7 +137,7 @@ static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) case DRM_FORMAT_NV12M: value |= ROT_CONTROL_FMT_YCBCR420_2P; break; - case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: value |= ROT_CONTROL_FMT_RGB888; break; default: @@ -167,16 +149,16 @@ static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt) } static void rotator_reg_set_flip(struct rot_context *rot, - enum drm_exynos_rot_flip flip) + enum drm_exynos_flip flip) { u32 value = readl(rot->regs + ROT_CONTROL); value &= ~ROT_CONTROL_FLIP_MASK; switch (flip) { - case ROT_FLIP_VERTICAL: + case EXYNOS_DRM_FLIP_VERTICAL: value |= ROT_CONTROL_FLIP_VERTICAL; break; - case ROT_FLIP_HORIZONTAL: + case EXYNOS_DRM_FLIP_HORIZONTAL: value |= ROT_CONTROL_FLIP_HORIZONTAL; break; default: @@ -188,19 +170,19 @@ static void rotator_reg_set_flip(struct rot_context *rot, } static void rotator_reg_set_rotation(struct rot_context *rot, - enum drm_exynos_rot_degree degree) + enum drm_exynos_degree degree) { u32 value = readl(rot->regs + ROT_CONTROL); value &= ~ROT_CONTROL_ROT_MASK; switch (degree) { - case ROT_DEGREE_90: + case EXYNOS_DRM_DEGREE_90: value |= ROT_CONTROL_ROT_90; break; - case ROT_DEGREE_180: + case EXYNOS_DRM_DEGREE_180: value |= ROT_CONTROL_ROT_180; break; - case ROT_DEGREE_270: + case EXYNOS_DRM_DEGREE_270: value |= ROT_CONTROL_ROT_270; break; default: @@ -247,16 +229,25 @@ static void rotator_reg_set_src_buf_addr(struct rot_context *rot, writel(addr, rot->regs + ROT_SRC_BUF_ADDR(i)); } +static void rotator_reg_get_src_buf_size(struct rot_context *rot, u32 *w, + u32 *h) +{ + u32 value = readl(rot->regs + ROT_SRC_BUF_SIZE); + + *w = ROT_GET_BUF_SIZE_W(value); + *h = ROT_GET_BUF_SIZE_H(value); +} + static void rotator_reg_set_src_buf_size(struct rot_context *rot, u32 w, u32 h) { - u32 value = ROT_SRC_BUF_SIZE_H(h) | ROT_SRC_BUF_SIZE_W(w); + u32 value = ROT_SET_BUF_SIZE_H(h) | ROT_SET_BUF_SIZE_W(w); writel(value, rot->regs + ROT_SRC_BUF_SIZE); } static void rotator_reg_set_src_crop_pos(struct rot_context *rot, u32 x, u32 y) { - u32 value = ROT_SRC_CROP_POS_Y(y) | ROT_SRC_CROP_POS_X(x); + u32 value = ROT_CROP_POS_Y(y) | ROT_CROP_POS_X(x); writel(value, rot->regs + ROT_SRC_CROP_POS); } @@ -274,16 +265,25 @@ static void rotator_reg_set_dst_buf_addr(struct rot_context *rot, writel(addr, rot->regs + ROT_DST_BUF_ADDR(i)); } +static void rotator_reg_get_dst_buf_size(struct rot_context *rot, u32 *w, + u32 *h) +{ + u32 value = readl(rot->regs + ROT_DST_BUF_SIZE); + + *w = ROT_GET_BUF_SIZE_W(value); + *h = ROT_GET_BUF_SIZE_H(value); +} + static void rotator_reg_set_dst_buf_size(struct rot_context *rot, u32 w, u32 h) { - u32 value = ROT_DST_BUF_SIZE_H(h) | ROT_DST_BUF_SIZE_W(w); + u32 value = ROT_SET_BUF_SIZE_H(h) | ROT_SET_BUF_SIZE_W(w); writel(value, rot->regs + ROT_DST_BUF_SIZE); } static void rotator_reg_set_dst_crop_pos(struct rot_context *rot, u32 x, u32 y) { - u32 value = ROT_DST_CROP_POS_Y(y) | ROT_DST_CROP_POS_X(x); + u32 value = ROT_CROP_POS_Y(y) | ROT_CROP_POS_X(x); writel(value, rot->regs + ROT_DST_CROP_POS); } @@ -294,71 +294,40 @@ static void rotator_reg_get_dump(struct rot_context *rot) for (i = 0; i <= ROT_DST_CROP_POS; i += 0x4) { value = readl(rot->regs + i); - DRM_INFO("+0x%x: 0x%x", i, value); + DRM_INFO("[%s] [0x%x] : 0x%x\n", __func__, i, value); } } -static bool rotator_check_format_n_handle_valid(u32 img_fmt, - u32 src_buf_handle_cnt, - u32 dst_buf_handle_cnt) +static irqreturn_t rotator_irq_handler(int irq, void *arg) { - bool ret = false; + struct rot_context *rot = arg; + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + enum rot_irq_status irq_status; - if ((src_buf_handle_cnt != dst_buf_handle_cnt) - || (src_buf_handle_cnt == 0)) - return ret; + /* Get execution result */ + irq_status = rotator_reg_get_irq_status(rot); + rotator_reg_set_irq_status_clear(rot, irq_status); - switch (img_fmt) { - case DRM_FORMAT_NV12M: - if (src_buf_handle_cnt == 2) - ret = true; - break; - case DRM_FORMAT_NV12: - case DRM_FORMAT_RGB888: - if (src_buf_handle_cnt == 1) - ret = true; - break; - default: - DRM_ERROR("invalid image format\n"); - break; + if (irq_status == ROT_IRQ_STATUS_COMPLETE) + ipp_send_event_handler(ippdrv, + rot->cur_buf_id[EXYNOS_DRM_OPS_DST]); + else { + DRM_ERROR("the SFR is set illegally\n"); + rotator_reg_get_dump(rot); } - return ret; -} - -static void rotator_align_size(struct rot_limit *limit, u32 mask, u32 *w, - u32 *h) -{ - u32 value; - - value = ROT_ALIGN(w, limit->align, mask); - if (value < limit->min_w) - *w = ROT_MIN(limit->min_w, mask); - else if (value > limit->max_w) - *w = ROT_MAX(limit->max_w, mask); - else - *w = value; - - value = ROT_ALIGN(h, limit->align, mask); - if (value < limit->min_h) - *h = ROT_MIN(limit->min_h, mask); - else if (value > limit->max_h) - *h = ROT_MAX(limit->max_h, mask); - else - *h = value; + return IRQ_HANDLED; } -static void rotator_align_buffer(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct drm_exynos_rot_control *control) +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize, + u32 *vsize) { struct rot_limit_table *limit_tbl = rot->limit_tbl; struct rot_limit *limit; - u32 mask; + u32 mask, value; /* Get size limit */ - if (control->img_fmt == DRM_FORMAT_RGB888) + if (fmt == ROT_CONTROL_FMT_RGB888) limit = &limit_tbl->rgb888; else limit = &limit_tbl->ycbcr420_2p; @@ -366,310 +335,333 @@ static void rotator_align_buffer(struct rot_context *rot, /* Get mask for rounding to nearest aligned value */ mask = ~((1 << limit->align) - 1); - /* For source buffer */ - buf->src_w = req_buf->src_w; - buf->src_h = req_buf->src_h; - rotator_align_size(limit, mask, &buf->src_w, &buf->src_h); + /* Set aligned width */ + value = ROT_ALIGN(*hsize, limit->align, mask); + if (value < limit->min_w) + *hsize = ROT_MIN(limit->min_w, mask); + else if (value > limit->max_w) + *hsize = ROT_MAX(limit->max_w, mask); + else + *hsize = value; - /* For destination buffer */ - buf->dst_w = req_buf->dst_w; - buf->dst_h = req_buf->dst_h; - rotator_align_size(limit, mask, &buf->dst_w, &buf->dst_h); + /* Set aligned height */ + value = ROT_ALIGN(*vsize, limit->align, mask); + if (value < limit->min_h) + *vsize = ROT_MIN(limit->min_h, mask); + else if (value > limit->max_h) + *vsize = ROT_MAX(limit->max_h, mask); + else + *vsize = value; } -static bool rotator_check_crop_boundary(struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) +static int rotator_src_set_fmt(struct device *dev, u32 fmt) { - bool ret = true; + struct rot_context *rot = dev_get_drvdata(dev); - /* Check source crop position */ - if ((crop->src_x + crop->src_w > buf->src_w) - || (crop->src_y + crop->src_h > buf->src_h)) - return false; + /* Set format configuration */ + rotator_reg_set_format(rot, fmt); - /* Check destination crop position */ - switch (control->degree) { - case ROT_DEGREE_90: - case ROT_DEGREE_270: - if ((crop->dst_x + crop->src_h > buf->dst_w) - || (crop->dst_y + crop->src_w > buf->dst_h)) - ret = false; - break; - default: - if ((crop->dst_x + crop->src_w > buf->dst_w) - || (crop->dst_y + crop->src_h > buf->dst_h)) - ret = false; - break; - } - - return ret; + return 0; } -static int rotator_iommu_map(struct rot_buffer *buf, - struct drm_exynos_rot_buffer *req_buf, - struct iommu_gem_map_params *params, - struct list_head *iommu_list) -{ - /* For source buffer */ - buf->src_cnt = 0; - while (buf->src_cnt < req_buf->src_cnt) { - buf->src_addr[buf->src_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->src_handle[buf->src_cnt], - IOMMU_ROTATOR); - if (!buf->src_addr[buf->src_cnt]) { - DRM_ERROR("failed to map src handle[%u]\n", - buf->src_cnt); - return -EINVAL; - } - buf->src_gem_obj[(buf->src_cnt)++] = params->gem_obj; - } +static int rotator_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, + struct drm_exynos_sz *sz) +{ + struct rot_context *rot = dev_get_drvdata(dev); + u32 fmt, hsize, vsize; - /* For destination buffer */ - buf->dst_cnt = 0; - while (buf->dst_cnt < req_buf->dst_cnt) { - buf->dst_addr[buf->dst_cnt] = exynos_drm_iommu_map_gem(params, - iommu_list, - req_buf->dst_handle[buf->dst_cnt], - IOMMU_ROTATOR); - if (!buf->dst_addr[buf->dst_cnt]) { - DRM_ERROR("failed to map dst handle[%u]\n", - buf->dst_cnt); - return -EINVAL; - } - buf->dst_gem_obj[(buf->dst_cnt)++] = params->gem_obj; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - return 0; -} + /* Align buffer size */ + hsize = sz->hsize; + vsize = sz->vsize; + rotator_align_size(rot, fmt, &hsize, &vsize); -static void rotator_iommu_unmap(struct rot_buffer *buf, - struct iommu_gem_map_params *params) -{ - /* For destination buffer */ - while (buf->dst_cnt > 0) { - params->gem_obj = buf->dst_gem_obj[--(buf->dst_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->dst_addr[buf->dst_cnt], - IOMMU_ROTATOR); - } + /* Set buffer size configuration */ + rotator_reg_set_src_buf_size(rot, hsize, vsize); - /* For source buffer */ - while (buf->src_cnt > 0) { - params->gem_obj = buf->src_gem_obj[--(buf->src_cnt)]; - exynos_drm_iommu_unmap_gem(params, - buf->src_addr[buf->src_cnt], - IOMMU_ROTATOR); - } + /* Set crop image position configuration */ + rotator_reg_set_src_crop_pos(rot, pos->x, pos->y); + rotator_reg_set_src_crop_size(rot, pos->w, pos->h); + + return 0; } -static void rotator_execute(struct rot_context *rot, - struct rot_buffer *buf, - struct drm_exynos_rot_control *control, - struct drm_exynos_rot_crop *crop) +static int rotator_src_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, + u32 buf_id, enum drm_exynos_ipp_buf_ctrl ctrl) { + struct rot_context *rot = dev_get_drvdata(dev); + dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; + u32 fmt, hsize, vsize; int i; - pm_runtime_get_sync(rot->subdrv.dev); - - /* Set interrupt enable */ - rotator_reg_set_irq(rot, true); + /* Set current buf_id */ + rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; - /* Set control registers */ - rotator_reg_set_format(rot, control->img_fmt); - rotator_reg_set_flip(rot, control->flip); - rotator_reg_set_rotation(rot, control->degree); + switch (ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* Set address configuration */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + addr[i] = buf_info->base[i]; - /* Set source buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_src_buf_addr(rot, buf->src_addr[i], i); + /* Get format */ + fmt = rotator_reg_get_format(rot); - /* Set source buffer size */ - rotator_reg_set_src_buf_size(rot, buf->src_w, buf->src_h); + /* Re-set cb planar for NV12 format */ + if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && + (addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) { + /* Get buf size */ + rotator_reg_get_src_buf_size(rot, &hsize, &vsize); - /* Set destination buffer address */ - for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++) - rotator_reg_set_dst_buf_addr(rot, buf->dst_addr[i], i); + /* Set cb planar */ + addr[EXYNOS_DRM_PLANAR_CB] = + addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; + } - /* Set destination buffer size */ - rotator_reg_set_dst_buf_size(rot, buf->dst_w, buf->dst_h); + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_src_buf_addr(rot, addr[i], i); + break; + case IPP_BUF_CTRL_DEQUEUE: + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_src_buf_addr(rot, buf_info->base[i], i); + break; + default: + /* Nothing to do */ + break; + } - /* Set source crop image position */ - rotator_reg_set_src_crop_pos(rot, crop->src_x, crop->src_y); + return 0; +} - /* Set source crop image size */ - rotator_reg_set_src_crop_size(rot, crop->src_w, crop->src_h); +static int rotator_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip) +{ + struct rot_context *rot = dev_get_drvdata(dev); - /* Set destination crop image position */ - rotator_reg_set_dst_crop_pos(rot, crop->dst_x, crop->dst_y); + /* Set transform configuration */ + rotator_reg_set_flip(rot, flip); + rotator_reg_set_rotation(rot, degree); - /* Start rotator operation */ - rotator_reg_set_start(rot); + /* Check degree for setting buffer size swap */ + if ((degree == EXYNOS_DRM_DEGREE_90) || + (degree == EXYNOS_DRM_DEGREE_270)) + return 1; + else + return 0; } -int exynos_drm_rotator_exec_ioctl(struct drm_device *drm_dev, void *data, - struct drm_file *file) +static int rotator_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, + struct drm_exynos_sz *sz) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct device *dev = priv->dev; - struct rot_context *rot; - struct drm_exynos_rot_exec_data *req = data; - struct drm_exynos_rot_buffer *req_buf = &req->buf; - struct drm_exynos_rot_control *control = &req->control; - struct drm_exynos_rot_crop *crop = &req->crop; - struct rot_buffer buf; - struct iommu_gem_map_params params; - - if (!dev) { - DRM_ERROR("failed to get dev\n"); - return -ENODEV; - } + struct rot_context *rot = dev_get_drvdata(dev); + u32 fmt, hsize, vsize; - rot = dev_get_drvdata(dev); - if (!rot) { - DRM_ERROR("failed to get drvdata\n"); - return -EFAULT; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - if (rot->suspended) { - DRM_ERROR("suspended state\n"); - return -EPERM; - } - - if (!rotator_check_format_n_handle_valid(control->img_fmt, - req_buf->src_cnt, - req_buf->dst_cnt)) { - DRM_ERROR("format or handles are invalid\n"); - return -EINVAL; - } + /* Align buffer size */ + hsize = sz->hsize; + vsize = sz->vsize; + rotator_align_size(rot, fmt, &hsize, &vsize); - init_completion(&rot->complete); + /* Set buffer size configuration */ + rotator_reg_set_dst_buf_size(rot, hsize, vsize); - /* Align buffer */ - rotator_align_buffer(rot, &buf, req_buf, control); + /* Set crop image position configuration */ + rotator_reg_set_dst_crop_pos(rot, pos->x, pos->y); - /* Check crop boundary */ - if (!rotator_check_crop_boundary(&buf, control, crop)) { - DRM_ERROR("boundary errror\n"); - return -EINVAL; - } + return 0; +} - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; +static int rotator_dst_set_addr(struct device *dev, + struct drm_exynos_ipp_buf_info *buf_info, + u32 buf_id, enum drm_exynos_ipp_buf_ctrl ctrl) +{ + struct rot_context *rot = dev_get_drvdata(dev); + dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; + u32 fmt, hsize, vsize; + int i; - /* Map IOMMU */ - rot->exec_ret = rotator_iommu_map(&buf, req_buf, ¶ms, - &priv->iommu_list); - if (rot->exec_ret < 0) - goto err_iommu_map; + /* Set current buf_id */ + rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; - /* Assign another src/dst_addr for NV12 image format */ - if (control->img_fmt == DRM_FORMAT_NV12) { - u32 size = crop->src_w * crop->src_h; + switch (ctrl) { + case IPP_BUF_CTRL_QUEUE: + /* Set address configuration */ + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + addr[i] = buf_info->base[i]; - buf.src_addr[buf.src_cnt + 1] = - buf.src_addr[buf.src_cnt] + size; - buf.dst_addr[buf.dst_cnt + 1] = - buf.dst_addr[buf.dst_cnt] + size; - } + /* Get format */ + fmt = rotator_reg_get_format(rot); - /* Execute */ - mutex_lock(&rot->exec_mutex); - rotator_execute(rot, &buf, control, crop); - if (!wait_for_completion_timeout(&rot->complete, 2 * HZ)) { - DRM_ERROR("timeout error\n"); - rot->exec_ret = -ETIMEDOUT; - mutex_unlock(&rot->exec_mutex); - goto err_iommu_map; - } - mutex_unlock(&rot->exec_mutex); + /* Re-set cb planar for NV12 format */ + if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && + (addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) { + /* Get buf size */ + rotator_reg_get_dst_buf_size(rot, &hsize, &vsize); - /* Unmap IOMMU */ - rotator_iommu_unmap(&buf, ¶ms); + /* Set cb planar */ + addr[EXYNOS_DRM_PLANAR_CB] = + addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; + } - return rot->exec_ret; + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_dst_buf_addr(rot, addr[i], i); + break; + case IPP_BUF_CTRL_DEQUEUE: + for (i = 0; i < EXYNOS_DRM_PLANAR_MAX; i++) + rotator_reg_set_dst_buf_addr(rot, buf_info->base[i], i); + break; + default: + /* Nothing to do */ + break; + } -err_iommu_map: - rotator_iommu_unmap(&buf, ¶ms); - return rot->exec_ret; + return 0; } -EXPORT_SYMBOL_GPL(exynos_drm_rotator_exec_ioctl); -static irqreturn_t rotator_irq_thread(int irq, void *arg) -{ - struct rot_context *rot = (struct rot_context *)arg; - enum rot_irq_status irq_status; - unsigned long flags; +static struct exynos_drm_ipp_ops rot_src_ops = { + .set_fmt = rotator_src_set_fmt, + .set_size = rotator_src_set_size, + .set_addr = rotator_src_set_addr, +}; - pm_qos_update_request(&rot->pm_qos, 0); +static struct exynos_drm_ipp_ops rot_dst_ops = { + .set_transf = rotator_dst_set_transf, + .set_size = rotator_dst_set_size, + .set_addr = rotator_dst_set_addr, +}; - /* Get execution result */ - spin_lock_irqsave(&rot->irq_lock, flags); - irq_status = rotator_reg_get_irq_status(rot); - rotator_reg_set_irq_status_clear(rot, irq_status); - spin_unlock_irqrestore(&rot->irq_lock, flags); +static int rotator_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + struct drm_exynos_ipp_config *src_config = + &property->config[EXYNOS_DRM_OPS_SRC]; + struct drm_exynos_ipp_config *dst_config = + &property->config[EXYNOS_DRM_OPS_DST]; + struct drm_exynos_pos *src_pos = &src_config->pos; + struct drm_exynos_pos *dst_pos = &dst_config->pos; + struct drm_exynos_sz *src_sz = &src_config->sz; + struct drm_exynos_sz *dst_sz = &dst_config->sz; + bool swap = false; + + /* Check format configuration */ + if (src_config->fmt != dst_config->fmt) { + DRM_DEBUG_KMS("[%s]not support csc feature\n", __func__); + return -EINVAL; + } - rot->exec_ret = 0; - if (irq_status != ROT_IRQ_STATUS_COMPLETE) { - DRM_ERROR("the SFR is set illegally\n"); - rot->exec_ret = -EINVAL; - rotator_reg_get_dump(rot); + switch (src_config->fmt) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12M: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]not support format\n", __func__); + return -EINVAL; } - pm_runtime_put(rot->subdrv.dev); + /* Check transform configuration */ + if (src_config->degree != EXYNOS_DRM_DEGREE_0) { + DRM_DEBUG_KMS("[%s]not support source-side rotation\n", + __func__); + return -EINVAL; + } - complete(&rot->complete); + switch (dst_config->degree) { + case EXYNOS_DRM_DEGREE_90: + case EXYNOS_DRM_DEGREE_270: + swap = true; + case EXYNOS_DRM_DEGREE_0: + case EXYNOS_DRM_DEGREE_180: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]invalid degree\n", __func__); + return -EINVAL; + } - return IRQ_HANDLED; -} + if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { + DRM_DEBUG_KMS("[%s]not support source-side flip\n", __func__); + return -EINVAL; + } -static int rotator_subdrv_open(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) -{ - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv; + switch (dst_config->flip) { + case EXYNOS_DRM_FLIP_NONE: + case EXYNOS_DRM_FLIP_VERTICAL: + case EXYNOS_DRM_FLIP_HORIZONTAL: + /* No problem */ + break; + default: + DRM_DEBUG_KMS("[%s]invalid flip\n", __func__); + return -EINVAL; + } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "failed to allocate priv\n"); - return -ENOMEM; + /* Check size configuration */ + if ((src_pos->x + src_pos->w > src_sz->hsize) || + (src_pos->y + src_pos->h > src_sz->vsize)) { + DRM_DEBUG_KMS("[%s]out of source buffer bound\n", __func__); + return -EINVAL; } - priv->dev = dev; - INIT_LIST_HEAD(&priv->iommu_list); + if (swap) { + if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || + (dst_pos->y + dst_pos->w > dst_sz->hsize)) { + DRM_DEBUG_KMS("[%s]out of destination buffer bound\n", + __func__); + return -EINVAL; + } - file_priv->rot_priv = priv; + if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { + DRM_DEBUG_KMS("[%s]not support scale feature\n", + __func__); + return -EINVAL; + } + } else { + if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || + (dst_pos->y + dst_pos->h > dst_sz->vsize)) { + DRM_DEBUG_KMS("[%s]out of destination buffer bound\n", + __func__); + return -EINVAL; + } + + if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { + DRM_DEBUG_KMS("[%s]not support scale feature\n", + __func__); + return -EINVAL; + } + } return 0; } -static void rotator_subdrv_close(struct drm_device *drm_dev, struct device *dev, - struct drm_file *file) +static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) { - struct drm_exynos_file_private *file_priv = file->driver_priv; - struct exynos_drm_rot_private *priv = file_priv->rot_priv; - struct iommu_gem_map_params params; - struct iommu_info_node *node, *n; + struct rot_context *rot = dev_get_drvdata(dev); - params.dev = dev; - params.drm_dev = drm_dev; - params.file = file; + if (rot->suspended) { + DRM_ERROR("suspended state\n"); + return -EPERM; + } - list_for_each_entry_safe(node, n, &priv->iommu_list, list) { - params.gem_obj = node->gem_obj; - exynos_drm_iommu_unmap_gem(¶ms, node->dma_addr, - IOMMU_ROTATOR); - list_del(&node->list); - kfree(node); - node = NULL; + if (cmd != IPP_CMD_M2M) { + DRM_ERROR("not support cmd: %d\n", cmd); + return -EINVAL; } - kfree(priv); + /* Set interrupt enable */ + rotator_reg_set_irq(rot, true); + + /* start rotator operation */ + rotator_reg_set_start(rot); - return; + return 0; } static int __devinit rotator_probe(struct platform_device *pdev) @@ -677,7 +669,7 @@ static int __devinit rotator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rot_context *rot; struct resource *res; - struct exynos_drm_subdrv *subdrv; + struct exynos_drm_ippdrv *ippdrv; int ret; rot = kzalloc(sizeof(*rot), GFP_KERNEL); @@ -689,21 +681,6 @@ static int __devinit rotator_probe(struct platform_device *pdev) rot->limit_tbl = (struct rot_limit_table *) platform_get_device_id(pdev)->driver_data; - mutex_init(&rot->exec_mutex); - spin_lock_init(&rot->irq_lock); - - ret = exynos_drm_iommu_setup(dev); - if (ret < 0) { - dev_err(dev, "failed to setup iommu\n"); - goto err_iommu_setup; - } - - ret = exynos_drm_iommu_activate(dev); - if (ret < 0) { - dev_err(dev, "failed to activate iommu\n"); - goto err_iommu_activate; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "failed to find registers\n"); @@ -733,7 +710,7 @@ static int __devinit rotator_probe(struct platform_device *pdev) goto err_get_irq; } - ret = request_threaded_irq(rot->irq, NULL, rotator_irq_thread, + ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot); if (ret < 0) { dev_err(dev, "failed to request irq\n"); @@ -748,26 +725,28 @@ static int __devinit rotator_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - pm_qos_add_request(&rot->pm_qos, PM_QOS_BUS_DMA_THROUGHPUT, 0); - subdrv = &rot->subdrv; - subdrv->dev = dev; - subdrv->open = rotator_subdrv_open; - subdrv->close = rotator_subdrv_close; + ippdrv = &rot->ippdrv; + ippdrv->dev = dev; + ippdrv->iommu_used = true; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; + ippdrv->check_property = rotator_ippdrv_check_property; + ippdrv->start = rotator_ippdrv_start; platform_set_drvdata(pdev, rot); - ret = exynos_drm_subdrv_register(subdrv); + ret = exynos_drm_ippdrv_register(ippdrv); if (ret < 0) { dev_err(dev, "failed to register drm rotator device\n"); - goto err_subdrv_register; + goto err_ippdrv_register; } dev_info(dev, "The exynos rotator is probed successfully\n"); return 0; -err_subdrv_register: +err_ippdrv_register: pm_runtime_disable(dev); clk_put(rot->clock); err_clk_get: @@ -778,23 +757,18 @@ err_ioremap: release_resource(rot->regs_res); kfree(rot->regs_res); err_get_resource: - exynos_drm_iommu_deactivate(dev); -err_iommu_activate: - exynos_drm_iommu_cleanup(dev); -err_iommu_setup: kfree(rot); return ret; } static int __devexit rotator_remove(struct platform_device *pdev) { - struct rot_context *rot = platform_get_drvdata(pdev); - - pm_qos_remove_request(&rot->pm_qos); + struct device *dev = &pdev->dev; + struct rot_context *rot = dev_get_drvdata(dev); - exynos_drm_subdrv_unregister(&rot->subdrv); + exynos_drm_ippdrv_unregister(&rot->ippdrv); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); clk_put(rot->clock); free_irq(rot->irq, rot); @@ -804,9 +778,6 @@ static int __devexit rotator_remove(struct platform_device *pdev) release_resource(rot->regs_res); kfree(rot->regs_res); - exynos_drm_iommu_deactivate(&pdev->dev); - exynos_drm_iommu_cleanup(&pdev->dev); - kfree(rot); return 0; @@ -841,14 +812,13 @@ struct platform_device_id rotator_driver_ids[] = { static int rotator_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - - /* Check & wait for running state */ - mutex_lock(&rot->exec_mutex); - mutex_unlock(&rot->exec_mutex); + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + struct drm_device *drm_dev = ippdrv->drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; rot->suspended = true; - exynos_drm_iommu_deactivate(dev); + exynos_drm_iommu_deactivate(drm_priv->vmm, dev); return 0; } @@ -856,12 +826,18 @@ static int rotator_suspend(struct device *dev) static int rotator_resume(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); + struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; + struct drm_device *drm_dev = ippdrv->drm_dev; + struct exynos_drm_private *drm_priv = drm_dev->dev_private; + int ret; - rot->suspended = false; + ret = exynos_drm_iommu_activate(drm_priv->vmm, dev); + if (ret) + DRM_ERROR("failed to activate iommu\n"); - exynos_drm_iommu_activate(dev); + rot->suspended = false; - return 0; + return ret; } #endif @@ -880,7 +856,6 @@ static int rotator_runtime_resume(struct device *dev) struct rot_context *rot = dev_get_drvdata(dev); clk_enable(rot->clock); - pm_qos_update_request(&rot->pm_qos, 400000); return 0; } |