oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vin/vin-vipp/sunxi_scaler.c

731 lines
20 KiB
C
Raw Normal View History

2022-05-07 01:01:45 +02:00
/*
* linux-3.10/drivers/media/platform/sunxi-vin/vin-vipp/sunxi_scaler.c
*
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
/*
*****************************************************************************
*
* sunxi_scaler.c
*
* Hawkview ISP - sunxi_scaler.c module
*
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Version Author Date Description
*
* 3.0 Zhao Wei 2014/12/11 ISP Tuning Tools Support
*
******************************************************************************
*/
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include "../platform/platform_cfg.h"
#include "sunxi_scaler.h"
#include "../vin-video/vin_core.h"
#include "vipp_reg.h"
#define SCALER_MODULE_NAME "vin_scaler"
#define ALIGN_4B(x) (((x) + (3)) & ~(3))
static LIST_HEAD(scaler_drv_list);
#define MIN_IN_WIDTH 32
#define MIN_IN_HEIGHT 32
#define MAX_IN_WIDTH 4095
#define MAX_IN_HEIGHT 4095
#define MIN_OUT_WIDTH 16
#define MIN_OUT_HEIGHT 2
#define MAX_OUT_WIDTH 4095
#define MAX_OUT_HEIGHT 4095
static struct v4l2_mbus_framefmt *__scaler_get_format(struct scaler_dev *scaler,
struct v4l2_subdev_fh *fh,
unsigned int pad,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
else
return &scaler->formats[pad];
}
static struct v4l2_rect *__scaler_get_crop(struct scaler_dev *scaler,
struct v4l2_subdev_fh *fh,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return fh ? v4l2_subdev_get_try_crop(fh, SCALER_PAD_SINK) : NULL;
else
return &scaler->crop.request;
}
static void __scaler_try_crop(const struct v4l2_mbus_framefmt *sink,
const struct v4l2_mbus_framefmt *source,
struct v4l2_rect *crop)
{
unsigned int min_width = source->width;
unsigned int min_height = source->height;
unsigned int max_width = sink->width;
unsigned int max_height = sink->height;
crop->width = clamp_t(u32, crop->width, min_width, max_width);
crop->height = clamp_t(u32, crop->height, min_height, max_height);
/* Crop can not go beyond of the input rectangle */
crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH);
crop->width = \
clamp_t(u32, crop->width, MIN_IN_WIDTH, sink->width - crop->left);
crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT);
crop->height = \
clamp_t(u32, crop->height, MIN_IN_HEIGHT, sink->height - crop->top);
}
static int __scaler_w_shift(int x_ratio, int y_ratio)
{
int m, n;
int sum_weight = 0;
int weight_shift = -8;
int xr = (x_ratio >> 8) + 1;
int yr = (y_ratio >> 8) + 1;
for (m = 0; m <= xr; m++) {
for (n = 0; n <= yr; n++) {
sum_weight += (y_ratio - abs((n << 8) - (yr << 7)))
* (x_ratio - abs((m << 8) - (xr << 7)));
}
}
sum_weight >>= 8;
while (sum_weight != 0) {
weight_shift++;
sum_weight >>= 1;
}
return weight_shift;
}
static void __scaler_calc_ratios(struct scaler_dev *scaler,
struct v4l2_rect *input,
struct v4l2_mbus_framefmt *output,
struct scaler_para *para)
{
unsigned int width;
unsigned int height;
unsigned int r_min;
output->width = clamp_t(u32, output->width, MIN_IN_WIDTH, input->width);
output->height = \
clamp_t(u32, output->height, MIN_IN_HEIGHT, input->height);
para->xratio = 256 * input->width / output->width;
para->yratio = 256 * input->height / output->height;
para->xratio = clamp_t(u32, para->xratio, 256, 2048);
para->yratio = clamp_t(u32, para->yratio, 256, 2048);
r_min = min(para->xratio, para->yratio);
#ifdef CROP_AFTER_SCALER
width = ALIGN_4B(256 * input->width / r_min);
height = 256 * input->height / r_min;
para->xratio = 256 * input->width / width;
para->yratio = 256 * input->height / height;
para->xratio = clamp_t(u32, para->xratio, 256, 2048);
para->yratio = clamp_t(u32, para->yratio, 256, 2048);
para->width = width;
para->height = height;
vin_log(VIN_LOG_SCALER, "para: xr = %d, yr = %d, w = %d, h = %d\n",
para->xratio, para->yratio, para->width, para->height);
output->width = width;
output->height = height;
#else
width = output->width * r_min / 256;
height = output->height * r_min / 256;
para->xratio = r_min;
para->yratio = r_min;
para->width = output->width;
para->height = output->height;
vin_log(VIN_LOG_SCALER, "para: xr = %d, yr = %d, w = %d, h = %d\n",
para->xratio, para->yratio, para->width, para->height);
/* Center the new crop rectangle.
* crop is before scaler
*/
input->left += (input->width - width) / 2;
input->top += (input->height - height) / 2;
input->width = width;
input->height = height;
#endif
vin_log(VIN_LOG_SCALER, "crop: left = %d, top = %d, w = %d, h = %d\n",
input->left, input->top, input->width, input->height);
}
static void __scaler_try_format(struct scaler_dev *scaler,
struct v4l2_subdev_fh *fh, unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
struct scaler_para ratio;
struct v4l2_rect crop;
switch (pad) {
case SCALER_PAD_SINK:
fmt->width =
clamp_t(u32, fmt->width, MIN_IN_WIDTH, MAX_IN_WIDTH);
fmt->height =
clamp_t(u32, fmt->height, MIN_IN_HEIGHT, MAX_IN_HEIGHT);
break;
case SCALER_PAD_SOURCE:
format =
__scaler_get_format(scaler, fh, SCALER_PAD_SINK, which);
fmt->code = format->code;
crop = *__scaler_get_crop(scaler, fh, which);
__scaler_calc_ratios(scaler, &crop, fmt, &ratio);
break;
}
}
static int sunxi_scaler_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
return 0;
}
static int sunxi_scaler_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
if (fse->index != 0)
return -EINVAL;
format.code = fse->code;
format.width = 1;
format.height = 1;
__scaler_try_format(scaler, fh, fse->pad, &format,
V4L2_SUBDEV_FORMAT_ACTIVE);
fse->min_width = format.width;
fse->min_height = format.height;
if (format.code != fse->code)
return -EINVAL;
format.code = fse->code;
format.width = -1;
format.height = -1;
__scaler_try_format(scaler, fh, fse->pad, &format,
V4L2_SUBDEV_FORMAT_ACTIVE);
fse->max_width = format.width;
fse->max_height = format.height;
return 0;
}
static int sunxi_scaler_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __scaler_get_format(scaler, fh, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
fmt->format = *format;
return 0;
}
static int sunxi_scaler_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
format = __scaler_get_format(scaler, fh, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
vin_log(VIN_LOG_FMT, "%s %d*%d %x %d\n", __func__, fmt->format.width,
fmt->format.height, fmt->format.code, fmt->format.field);
__scaler_try_format(scaler, fh, fmt->pad, &fmt->format, fmt->which);
*format = fmt->format;
if (fmt->pad == SCALER_PAD_SINK) {
/* reset crop rectangle */
crop = __scaler_get_crop(scaler, fh, fmt->which);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
crop->height = fmt->format.height;
/* Propagate the format from sink to source */
format = __scaler_get_format(scaler, fh, SCALER_PAD_SOURCE,\
fmt->which);
*format = fmt->format;
__scaler_try_format(scaler, fh, SCALER_PAD_SOURCE, format,\
fmt->which);
}
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
scaler->crop.active = scaler->crop.request;
__scaler_calc_ratios(scaler, &scaler->crop.active, format,\
&scaler->para);
}
/*return the format for other subdev*/
fmt->format = *format;
return 0;
}
static int sunxi_scaler_subdev_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format_source;
struct v4l2_mbus_framefmt *format_sink;
struct scaler_para para;
vin_print("%s\n", __func__);
if (sel->pad != SCALER_PAD_SINK)
return -EINVAL;
format_sink = \
__scaler_get_format(scaler, fh, SCALER_PAD_SINK, sel->which);
format_source = \
__scaler_get_format(scaler, fh, SCALER_PAD_SOURCE, sel->which);
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = INT_MAX;
sel->r.height = INT_MAX;
__scaler_try_crop(format_sink, format_source, &sel->r);
__scaler_calc_ratios(scaler, &sel->r, format_source, &para);
break;
case V4L2_SEL_TGT_CROP:
sel->r = *__scaler_get_crop(scaler, fh, sel->which);
__scaler_calc_ratios(scaler, &sel->r, format_source, &para);
break;
default:
return -EINVAL;
}
return 0;
}
static int sunxi_scaler_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format_sink, *format_source;
struct scaler_para para;
if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != SCALER_PAD_SINK)
return -EINVAL;
format_sink = \
__scaler_get_format(scaler, fh, SCALER_PAD_SINK, sel->which);
format_source = \
__scaler_get_format(scaler, fh, SCALER_PAD_SOURCE, sel->which);
vin_print("%s: L = %d, T = %d, W = %d, H = %d\n", __func__,
sel->r.left, sel->r.top, sel->r.width, sel->r.height);
vin_print("%s: input = %dx%d, output = %dx%d\n", __func__,
format_sink->width, format_sink->height,
format_source->width, format_source->height);
__scaler_try_crop(format_sink, format_source, &sel->r);
*__scaler_get_crop(scaler, fh, sel->which) = sel->r;
__scaler_calc_ratios(scaler, &sel->r, format_source, &para);
if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
scaler->para = para;
scaler->crop.active = sel->r;
vin_log(VIN_LOG_SCALER, "active crop: l = %d, t = %d, w = %d, h = %d\n",
scaler->crop.active.left, scaler->crop.active.top,
scaler->crop.active.width, scaler->crop.active.height);
return 0;
}
int sunxi_scaler_subdev_init(struct v4l2_subdev *sd, u32 val)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
vin_print("%s, val = %d.\n", __func__, val);
if (val) {
vipp_set_reg_load_addr(scaler->id, (unsigned long)scaler->vipp_reg.dma_addr);
vipp_set_osd_para_load_addr(scaler->id, (unsigned long)scaler->osd_para.dma_addr);
vipp_set_osd_stat_load_addr(scaler->id, (unsigned long)scaler->osd_stat.dma_addr);
vipp_set_osd_cv_update(scaler->id, NOT_UPDATED);
vipp_set_osd_ov_update(scaler->id, NOT_UPDATED);
vipp_set_para_ready(scaler->id, NOT_READY);
}
return 0;
}
static int sunxi_scaler_subdev_s_power(struct v4l2_subdev *sd, int enable)
{
return 0;
}
static int sunxi_scaler_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &scaler->formats[SCALER_PAD_SOURCE];
struct mbus_framefmt_res *res = (void *)scaler->formats[SCALER_PAD_SOURCE].reserved;
struct vipp_scaler_config scaler_cfg;
struct vipp_scaler_size scaler_size;
struct vipp_crop crop;
enum vipp_format out_fmt;
switch (res->res_pix_fmt) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
vin_print("%s output fmt is raw, return directly\n", __func__);
return 0;
default:
break;
}
if (mf->field == V4L2_FIELD_INTERLACED || mf->field == V4L2_FIELD_TOP ||
mf->field == V4L2_FIELD_BOTTOM) {
vin_print("Scaler not support field mode, return directly!\n");
return 0;
}
if (enable) {
crop.hor = scaler->crop.active.left;
crop.ver = scaler->crop.active.top;
crop.width = scaler->crop.active.width;
crop.height = scaler->crop.active.height;
vipp_set_crop(scaler->id, &crop);
scaler_size.sc_width = scaler->para.width;
scaler_size.sc_height = scaler->para.height;
vipp_scaler_output_size(scaler->id, &scaler_size);
switch (res->res_pix_fmt) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV420M:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YVU420M:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV21M:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M:
out_fmt = YUV420;
break;
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
out_fmt = YUV422;
break;
default:
out_fmt = YUV420;
break;
}
if (scaler->is_osd_en)
scaler_cfg.sc_out_fmt = YUV422;
else
scaler_cfg.sc_out_fmt = out_fmt;
scaler_cfg.sc_x_ratio = scaler->para.xratio;
scaler_cfg.sc_y_ratio = scaler->para.yratio;
scaler_cfg.sc_w_shift = __scaler_w_shift(scaler->para.xratio, scaler->para.yratio);
vipp_scaler_cfg(scaler->id, &scaler_cfg);
vipp_output_fmt_cfg(scaler->id, out_fmt);
vipp_scaler_en(scaler->id, 1);
vipp_set_para_ready(scaler->id, HAS_READY);
vipp_set_osd_ov_update(scaler->id, HAS_UPDATED);
vipp_set_osd_cv_update(scaler->id, HAS_UPDATED);
vipp_top_clk_en(scaler->id, enable);
vipp_enable(scaler->id);
} else {
vipp_disable(scaler->id);
vipp_top_clk_en(scaler->id, enable);
vipp_scaler_en(scaler->id, 0);
vipp_set_para_ready(scaler->id, NOT_READY);
vipp_set_osd_ov_update(scaler->id, NOT_UPDATED);
vipp_set_osd_cv_update(scaler->id, NOT_UPDATED);
}
vin_print("%s on = %d, %d*%d xr = %d yr = %d\n", __func__, enable,
scaler->para.width, scaler->para.height,
scaler->para.xratio, scaler->para.yratio);
return 0;
}
static const struct v4l2_subdev_core_ops sunxi_scaler_subdev_core_ops = {
.s_power = sunxi_scaler_subdev_s_power,
.init = sunxi_scaler_subdev_init,
};
static const struct v4l2_subdev_video_ops sunxi_scaler_subdev_video_ops = {
.s_stream = sunxi_scaler_subdev_s_stream,
};
/* subdev pad operations */
static const struct v4l2_subdev_pad_ops sunxi_scaler_subdev_pad_ops = {
.enum_mbus_code = sunxi_scaler_enum_mbus_code,
.enum_frame_size = sunxi_scaler_enum_frame_size,
.get_fmt = sunxi_scaler_subdev_get_fmt,
.set_fmt = sunxi_scaler_subdev_set_fmt,
.get_selection = sunxi_scaler_subdev_get_selection,
.set_selection = sunxi_scaler_subdev_set_selection,
};
static struct v4l2_subdev_ops sunxi_scaler_subdev_ops = {
.core = &sunxi_scaler_subdev_core_ops,
.video = &sunxi_scaler_subdev_video_ops,
.pad = &sunxi_scaler_subdev_pad_ops,
};
int __scaler_init_subdev(struct scaler_dev *scaler)
{
int ret;
struct v4l2_subdev *sd = &scaler->subdev;
mutex_init(&scaler->subdev_lock);
v4l2_subdev_init(sd, &sunxi_scaler_subdev_ops);
sd->grp_id = VIN_GRP_ID_SCALER;
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "sunxi_scaler.%u", scaler->id);
v4l2_set_subdevdata(sd, scaler);
/*sd->entity->ops = &isp_media_ops;*/
scaler->scaler_pads[SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
scaler->scaler_pads[SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ret = media_entity_init(&sd->entity, SCALER_PAD_NUM,
scaler->scaler_pads, 0);
if (ret < 0)
return ret;
return 0;
}
static int scaler_resource_alloc(struct scaler_dev *scaler)
{
int ret = 0;
scaler->vipp_reg.size = VIPP_REG_SIZE;
scaler->osd_para.size = OSD_PARA_SIZE;
scaler->osd_stat.size = OSD_STAT_SIZE;
ret = os_mem_alloc(&scaler->pdev->dev, &scaler->vipp_reg);
if (ret < 0) {
vin_err("scaler regs load addr requset failed!\n");
return -ENOMEM;
}
ret = os_mem_alloc(&scaler->pdev->dev, &scaler->osd_para);
if (ret < 0) {
vin_err("scaler para load addr requset failed!\n");
return -ENOMEM;
}
ret = os_mem_alloc(&scaler->pdev->dev, &scaler->osd_stat);
if (ret < 0) {
vin_err("scaler statistic load addr requset failed!\n");
return -ENOMEM;
}
return 0;
}
static void scaler_resource_free(struct scaler_dev *scaler)
{
os_mem_free(&scaler->pdev->dev, &scaler->vipp_reg);
os_mem_free(&scaler->pdev->dev, &scaler->osd_para);
os_mem_free(&scaler->pdev->dev, &scaler->osd_stat);
}
static int scaler_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct scaler_dev *scaler = NULL;
int ret = 0;
if (np == NULL) {
vin_err("Scaler failed to get of node\n");
return -ENODEV;
}
scaler = kzalloc(sizeof(struct scaler_dev), GFP_KERNEL);
if (!scaler) {
ret = -ENOMEM;
vin_err("sunxi scaler kzalloc failed!\n");
goto ekzalloc;
}
of_property_read_u32(np, "device_id", &pdev->id);
if (pdev->id < 0) {
vin_err("Scaler failed to get device id\n");
ret = -EINVAL;
goto freedev;
}
scaler->id = pdev->id;
scaler->pdev = pdev;
scaler->base = of_iomap(np, 0);
if (!scaler->base) {
scaler->is_empty = 1;
scaler->base = kzalloc(0x400, GFP_KERNEL);
if (!scaler->base) {
ret = -EIO;
goto freedev;
}
}
list_add_tail(&scaler->scaler_list, &scaler_drv_list);
__scaler_init_subdev(scaler);
spin_lock_init(&scaler->slock);
init_waitqueue_head(&scaler->wait);
if (scaler_resource_alloc(scaler) < 0) {
ret = -ENOMEM;
goto unmap;
}
vipp_set_base_addr(scaler->id, (unsigned long)scaler->base);
vipp_map_reg_load_addr(scaler->id, (unsigned long)scaler->vipp_reg.vir_addr);
vipp_map_osd_para_load_addr(scaler->id, (unsigned long)scaler->osd_para.vir_addr);
memset(scaler->vipp_reg.vir_addr, 0, VIPP_REG_SIZE);
memset(scaler->osd_para.vir_addr, 0, OSD_PARA_SIZE);
memset(scaler->osd_stat.vir_addr, 0, OSD_STAT_SIZE);
platform_set_drvdata(pdev, scaler);
vin_print("scaler%d probe end\n", scaler->id);
return 0;
unmap:
if (!scaler->is_empty)
iounmap(scaler->base);
else
kfree(scaler->base);
freedev:
kfree(scaler);
ekzalloc:
vin_err("scaler%d probe err!\n", scaler->id);
return ret;
}
static int scaler_remove(struct platform_device *pdev)
{
struct scaler_dev *scaler = platform_get_drvdata(pdev);
struct v4l2_subdev *sd = &scaler->subdev;
scaler_resource_free(scaler);
platform_set_drvdata(pdev, NULL);
v4l2_device_unregister_subdev(sd);
v4l2_set_subdevdata(sd, NULL);
list_del(&scaler->scaler_list);
if (scaler->base) {
if (!scaler->is_empty)
iounmap(scaler->base);
else
kfree(scaler->base);
}
kfree(scaler);
return 0;
}
static const struct of_device_id sunxi_scaler_match[] = {
{.compatible = "allwinner,sunxi-scaler",},
{},
};
static struct platform_driver scaler_platform_driver = {
.probe = scaler_probe,
.remove = scaler_remove,
.driver = {
.name = SCALER_MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = sunxi_scaler_match,
}
};
struct v4l2_subdev *sunxi_scaler_get_subdev(int id)
{
struct scaler_dev *scaler;
list_for_each_entry(scaler, &scaler_drv_list, scaler_list) {
if (scaler->id == id) {
scaler->use_cnt++;
return &scaler->subdev;
}
}
return NULL;
}
int sunxi_vipp_get_osd_stat(int id, unsigned int *stat)
{
struct v4l2_subdev *sd = sunxi_scaler_get_subdev(id);
struct scaler_dev *vipp = v4l2_get_subdevdata(sd);
unsigned long long *stat_buf = vipp->osd_stat.vir_addr;
int i;
if (stat_buf == NULL || stat == NULL)
return -EINVAL;
for (i = 0; i < MAX_OVERLAY_NUM; i++)
stat[i] = stat_buf[i];
return 0;
}
int sunxi_osd_change_sc_fmt(int id, enum vipp_format out_fmt, int en)
{
struct v4l2_subdev *sd = sunxi_scaler_get_subdev(id);
struct scaler_dev *vipp = v4l2_get_subdevdata(sd);
vipp->is_osd_en = en;
vipp_scaler_output_fmt(id, out_fmt);
return 0;
}
int sunxi_scaler_platform_register(void)
{
return platform_driver_register(&scaler_platform_driver);
}
void sunxi_scaler_platform_unregister(void)
{
platform_driver_unregister(&scaler_platform_driver);
vin_print("scaler_exit end\n");
}