From patchwork Fri Sep 14 12:46:39 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Lad, Prabhakar" X-Patchwork-Id: 1458801 Return-Path: X-Original-To: patchwork-davinci@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from arroyo.ext.ti.com (arroyo.ext.ti.com [192.94.94.40]) by patchwork2.kernel.org (Postfix) with ESMTP id 6DBEFDF280 for ; Fri, 14 Sep 2012 12:59:19 +0000 (UTC) Received: from dlelxv30.itg.ti.com ([172.17.2.17]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id q8ECsE47023726; Fri, 14 Sep 2012 07:54:30 -0500 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dlelxv30.itg.ti.com (8.13.8/8.13.8) with ESMTP id q8ECsEEq004367; Fri, 14 Sep 2012 07:54:14 -0500 Received: from dlelxv23.itg.ti.com (172.17.1.198) by dfle73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.1.323.3; Fri, 14 Sep 2012 07:54:14 -0500 Received: from linux.omap.com (dlelxs01.itg.ti.com [157.170.227.31]) by dlelxv23.itg.ti.com (8.13.8/8.13.8) with ESMTP id q8ECsELA006319; Fri, 14 Sep 2012 07:54:14 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 08ED280627; Fri, 14 Sep 2012 07:54:14 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp51.itg.ti.com (dflp51.itg.ti.com [128.247.22.94]) by linux.omap.com (Postfix) with ESMTP id 2F80C8062F for ; Fri, 14 Sep 2012 07:50:11 -0500 (CDT) Received: from neches.ext.ti.com (neches.ext.ti.com [192.91.81.29]) by dflp51.itg.ti.com (8.13.7/8.13.8) with ESMTP id q8ECoB1G002575 for ; Fri, 14 Sep 2012 07:50:11 -0500 (CDT) Received: from psmtp.com (na3sys009amx172.postini.com [74.125.149.98]) by neches.ext.ti.com (8.13.7/8.13.7) with SMTP id q8ECo9Ub028410 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 14 Sep 2012 07:50:09 -0500 Received: from mail-pb0-f45.google.com ([209.85.160.45]) (using TLSv1) by na3sys009amx172.postini.com ([74.125.148.10]) with SMTP; Fri, 14 Sep 2012 12:50:09 GMT Received: by mail-pb0-f45.google.com with SMTP id rp12so5836930pbb.4 for ; Fri, 14 Sep 2012 05:50:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=znje5YtJdhorM6MHGVX9OpdLgdb2uEcnosfv3WQncis=; b=zBJqJX/3pEhh7gM6WJ88SdkqVABz5jTXd3+FH3uP8BS4gR+OIhrKgJ7li9ezAkCurh rv8eDYzRAkg3madr17zWBhW5mfPIoVaUDHB2iQa3Z7Rk1doTWH86bVJdmZ7+DRMRrZOd hxKkOOiO+0C8Dtm3JRt0I0LTefUJGO4uUnsnd8zyk/PUdqOna9cLZiIZ5ctX+yqaxWu2 v8f4FxEcTGdL6izAI6XhBcCEQwFK5bZhyRZcFu5GThpzxZhagpT6XMuN1Vu/iPV4jVqg /zC1mdtzGeRF/9hcu10mQJJnYyPu/tzWsrhqDZhEVoAw4fyiYkUID7qS3NVBEWdmNRvK aKfw== Received: by 10.68.213.5 with SMTP id no5mr5218216pbc.24.1347627009174; Fri, 14 Sep 2012 05:50:09 -0700 (PDT) Received: from localhost.localdomain ([122.166.13.141]) by mx.google.com with ESMTPS id gv1sm939033pbc.38.2012.09.14.05.50.03 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 14 Sep 2012 05:50:08 -0700 (PDT) From: Prabhakar Lad To: LMML Subject: [PATCH 09/14] davinci: vpfe: resizer driver based on media framework Date: Fri, 14 Sep 2012 18:16:39 +0530 Message-ID: <1347626804-5703-10-git-send-email-prabhakar.lad@ti.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1347626804-5703-1-git-send-email-prabhakar.lad@ti.com> References: <1347626804-5703-1-git-send-email-prabhakar.lad@ti.com> X-pstn-levels: (S:24.98904/99.90000 CV:99.9000 FC:95.5390 LC:95.5390 R:95.9108 P:95.9108 M:97.0282 C:98.6951 ) X-pstn-dkim: 1 skipped:not-enabled X-pstn-settings: 2 (0.5000:0.0050) s cv GT3 gt2 gt1 r p m c X-pstn-addresses: from [82/3] CC: DLOS , LKML , Mauro Carvalho Chehab X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com From: Manjunath Hadli Add the video resizer driver with the v4l2 media controller framework which takes care of resizing the video frames with both up-scaling downscaling facility. The formats that is supported is YUV422. The driver supports both continuous mode where it works in tandem with the CCDC and previewer and single shot mode where it can be used in isolation or with previewer. The driver underneath uses the dm365 IPIPE module for programming the hardware. The driver supports resizer as a subdevice and a media entity, and the enumerable pads are 2(1 input and 1 output). The driver supports streaming and all the pad and link related operations. Specific filter functionality including filter types are set through private ioctls. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar --- drivers/media/platform/davinci/vpfe_resizer.c | 1080 +++++++++++++++++++++++++ drivers/media/platform/davinci/vpfe_resizer.h | 66 ++ 2 files changed, 1146 insertions(+), 0 deletions(-) create mode 100644 drivers/media/platform/davinci/vpfe_resizer.c create mode 100644 drivers/media/platform/davinci/vpfe_resizer.h diff --git a/drivers/media/platform/davinci/vpfe_resizer.c b/drivers/media/platform/davinci/vpfe_resizer.c new file mode 100644 index 0000000..8b98ff5 --- /dev/null +++ b/drivers/media/platform/davinci/vpfe_resizer.c @@ -0,0 +1,1080 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vpfe_mc_capture.h" +#include "imp_hw_if.h" + + +#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 + +/* resizer pixel formats */ +static const unsigned int resz_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, +#endif +}; + +static const unsigned int resz_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, +#endif +}; + +static char resizer_chained; +/* + * imp_set_resizer_config() - set resizer config + * @resizer: vpfe resizer device pointer + * @channel: image processor logical channel + * @chan_config: resizer channel configuration + */ +static int imp_set_resizer_config(struct vpfe_resizer_device *resizer, + struct imp_logical_channel *channel, + struct vpfe_rsz_config *chan_config) +{ + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct device *dev = resizer->subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + int ret; + + if (channel->config_state == STATE_NOT_CONFIGURED) { + channel->config = + imp_hw_if->alloc_config_block(dev, vpfe_dev->ipipe); + + if (!channel->config) { + dev_err(dev, + "Failed to allocate memory for channel config\n"); + return -EFAULT; + } + /* allocate buffer for holding user configuration */ + channel->user_config = imp_hw_if->alloc_user_config_block(dev, + vpfe_dev->ipipe, IMP_RESIZER); + + if (!channel->user_config) { + dev_err(dev, + "Failed to allocate memory for user config\n"); + if (!resizer_chained) + kfree(channel->config); + return -EFAULT; + } + } + + if (!chan_config->config) { + /* put defaults for user configuration */ + imp_hw_if->set_user_config_defaults(dev, vpfe_dev->ipipe, + IMP_RESIZER, channel->user_config); + + dev_dbg(dev, + "imp_set_resizer_config: default configuration set\n"); + } else { + if (copy_from_user(channel->user_config, chan_config->config, + sizeof(struct rsz_config))) + return -EFAULT; + dev_dbg(dev, + "imp_set_resizer_config: user configuration set\n"); + } + + /* Update the user configuration in the hw config block or + if chained, copy it to the shared block and allow previewer + to configure it */ + ret = imp_hw_if->set_resizer_config(dev, vpfe_dev->ipipe, + resizer_chained, + channel->user_config, + channel->config); + + if (ret < 0) + dev_err(dev, "Failed to set resizer configuration\n"); + + channel->config_state = STATE_CONFIGURED; + + return ret; +} + +/* + * imp_get_resizer_config() - set resizer config + * @resizer: vpfe resizer device pointer + * @channel: image processor logical channel + * @chan_config: resizer channel configuration + */ +static int imp_get_resize_config(struct vpfe_resizer_device *resizer, + struct imp_logical_channel *channel, + struct vpfe_rsz_config *chan_config) +{ + struct device *dev = resizer->subdev.v4l2_dev->dev; + + dev_dbg(dev, "imp_get_resize_config\n"); + + if (channel->config_state != STATE_CONFIGURED) { + dev_err(dev, "Resizer channel not configured\n"); + return -EINVAL; + } + + if (!chan_config->config) { + dev_err(dev, "Resizer channel invalid pointer\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)channel->user_config, + sizeof(struct rsz_config))) { + dev_err(dev, "imp_get_resize_config: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * set_resizer_channel_cont_mode() - Set continous channel mode + * @resizer: vpfe resizer device pointer + */ +static void set_resizer_channel_cont_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + + channel->config_state = STATE_NOT_CONFIGURED; + channel->user_config = NULL; +} + +/* + * set_resizer_channel_ss_mode() - Set single-shot channel mode + * @resizer: vpfe resizer device pointer + */ +static int set_resizer_channel_ss_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + + channel->config_state = STATE_NOT_CONFIGURED; + channel->user_config = NULL; + return imp_hw_if->set_oper_mode(vpfe_dev->ipipe, IMP_MODE_SINGLE_SHOT); +} + +/* + * reset_resizer_channel_mode() - Reset channel mode + * @resizer: vpfe resizer device pointer + */ +static void reset_resizer_channel_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + + if (resizer->input == RESIZER_INPUT_MEMORY) + imp_hw_if->reset_oper_mode(vpfe_dev->ipipe); + + if (channel->config_state == STATE_CONFIGURED) { + kfree(channel->user_config); + channel->user_config = NULL; + } +} + +/* + * VPFE video operations + */ + +/* rsz_video_in_queue() - RESIZER video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_in_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct imp_logical_channel *chan = &resizer->channel; + + if (resizer->input == RESIZER_INPUT_MEMORY) + imp_hw_if->update_inbuf_address(chan->config, addr); + else + imp_hw_if->update_inbuf_address(NULL, addr); +} + +/* + * rsz_video_out1_queue() - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_out1_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct imp_logical_channel *chan = &resizer->channel; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, + chan->config, addr); + else + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, NULL, addr); + +} + +/* + * rsz_video_out2_queue() - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_out2_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct imp_logical_channel *chan = &resizer->channel; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) + imp_hw_if->update_outbuf2_address(vpfe_dev->ipipe, + chan->config, addr); + else + imp_hw_if->update_outbuf2_address(vpfe_dev->ipipe, NULL, addr); +} + +static const struct vpfe_video_operations video_in_ops = { + .queue = rsz_video_in_queue, +}; + +static const struct vpfe_video_operations video_out1_ops = { + .queue = rsz_video_out1_queue, +}; + +static const struct vpfe_video_operations video_out2_ops = { + .queue = rsz_video_out2_queue, +}; + +/* + * rsz_ss_buffer_isr() - RESIZER module single-shot buffer scheduling isr + * @resizer: RESIZER device pointer + */ +static void rsz_ss_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_video_device *video_in = &resizer->video_in; + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_logical_channel *chan = &resizer->channel; + struct vpfe_pipeline *pipe = &video_out->pipe; + u32 val; + + if (resizer->output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->input == RESIZER_INPUT_MEMORY) { + spin_lock(&video_in->dma_queue_lock); + vpfe_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); + } + + if (resizer->output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->output2 == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (is_pipe_ready(pipe) && resizer->output == RESIZER_OUPUT_MEMORY) + imp_hw_if->enable(vpfe_dev->ipipe, 1, chan->config); +} + +/* + * rsz_buffer_isr() - RESIZER module buffer scheduling isr + * @resizer: RESIZER device pointer + */ +void rsz_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->input != RESIZER_INPUT_PREVIEWER) + return; + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + if (imp_hw_if->enable_resize) + imp_hw_if->enable_resize(1); + } else { + if (imp_hw_if->enable_resize) + imp_hw_if->enable_resize(0); + } + return; + } + + /* handle interlaced frame capture */ + fid = ccdc_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * rsz_dma_isr() - RESIZER module dma isr + * @resizer: RESIZER device pointer + */ +void rsz_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + enum v4l2_field field; + int schedule_capture = 0; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + rsz_ss_buffer_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = ccdc_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl() - RESIZER module private ioctl's + * @sd: VPFE RESZIER V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct imp_logical_channel *rsz_conf_chan = &resizer->channel; + struct device *dev = resizer->subdev.v4l2_dev->dev; + struct vpfe_rsz_config *user_config; + int ret = 0; + + switch (cmd) { + case VIDIOC_VPFE_RSZ_S_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + + dev_dbg(dev, "VIDIOC_VPFE_RSZ_S_CONFIG:\n"); + ret = imp_set_resizer_config(resizer, rsz_conf_chan, + user_config); + break; + case VIDIOC_VPFE_RSZ_G_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + dev_dbg(dev, "VIDIOC_VPFE_RSZ_G_CONFIG\n"); + if (!user_config->config) { + dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); + goto ERROR; + } + ret = imp_get_resize_config(resizer, rsz_conf_chan, + user_config); + break; + default: + ret = -ENOIOCTLCMD; + } + +ERROR: + return ret; +} + +/* + * resizer_set_stream() - Enable/Disable streaming on the RESIZER module + * @sd: VPFE RESIZER V4L2 subdevice + * @enable: Enable/disable stream + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_logical_channel *chan = &resizer->channel; + struct device *dev = resizer->subdev.v4l2_dev->dev; + + void *config = (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) ? + chan->config : NULL; + + if (resizer->output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT && + imp_hw_if->serialize(vpfe_dev->ipipe)) { + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, + config) < 0) + return -EINVAL; + } else if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + imp_hw_if->lock_chain(vpfe_dev->ipipe); + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, NULL)) + return -EINVAL; + } + imp_hw_if->enable(vpfe_dev->ipipe, 1, config); + break; + case 0: + imp_hw_if->enable(vpfe_dev->ipipe, 0, config); + if ((pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)) + imp_hw_if->unlock_chain(vpfe_dev->ipipe); + break; + } + + return 0; +} + +/* + * __resizer_get_format() - helper function for getting resizer format + * @res : pointer to resizer private structure + * @pad : pad number + * @fh : V4L2 subdev file handle + * @which : wanted subdev format + * Retun wanted mbus frame format + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct vpfe_resizer_device *res, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + return &res->formats[pad]; +} + +/* + * resizer_try_format() - Handle try format by pad subdev method + * @res : VPFE resizer device + * @fh : V4L2 subdev file handle + * @pad : pad num + * @fmt : pointer to v4l2 format structure + * @which : wanted subdev format + */ +static void resizer_try_format(struct vpfe_resizer_device *res, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct imp_hw_interface *imp_hw_if; + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + imp_hw_if = res->imp_hw_if; + + if (pad == RESIZER_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(resz_input_fmts); i++) { + if (fmt->code == resz_input_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_input_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + 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); + } else if (pad == RESIZER_PAD_SOURCE) { + max_out_width = imp_hw_if->get_max_output_width(0); + max_out_height = imp_hw_if->get_max_output_height(0); + + for (i = 0; i < ARRAY_SIZE(resz_output_fmts); i++) { + if (fmt->code == resz_output_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (pad == RESIZER_PAD_SOURCE2) { + max_out_width = imp_hw_if->get_max_output_width(1); + max_out_height = imp_hw_if->get_max_output_height(1); + + for (i = 0; i < ARRAY_SIZE(resz_output_fmts); i++) { + if (fmt->code == resz_output_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* +* resizer_set_format() - set format on pad +* @sd : VPFE resizer device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +*/ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(resizer, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == RESIZER_PAD_SINK && + resizer->input == RESIZER_INPUT_PREVIEWER) { + /* + *This is continous mode, input format is already set in + *PREVIEWER sink set format, no need to do here. + */ + resizer->formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_PAD_SOURCE && + resizer->output == RESIZER_OUPUT_MEMORY) { + + imp_hw_if->set_out_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + + } else if (fmt->pad == RESIZER_PAD_SINK && + resizer->input == RESIZER_INPUT_MEMORY) { + /* + * single shot mode + */ + imp_hw_if->set_in_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_PAD_SOURCE2 && + resizer->output2 == RESIZER_OUPUT_MEMORY) { + imp_hw_if->set_out2_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format() - Retrieve the video format on a pad + * @sd : VPFE RESIZER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size() - enum frame sizes on pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_resizer_device *res = 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; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + 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; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code() - enum mbus codes for pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resz_input_fmts)) + return -EINVAL; + + code->code = resz_input_fmts[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resz_output_fmts)) + return -EINVAL; + + code->code = resz_output_fmts[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats() - Initialize formats on all pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct vpfe_resizer_device *res = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = res->imp_hw_if; + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = imp_hw_if->get_max_output_width(0); + format.format.height = imp_hw_if->get_max_output_height(0); + resizer_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup() - Setup RESIZER connections + * @entity: RESIZER media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from previewer - continous mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + reset_resizer_channel_mode(resizer); + resizer->input = RESIZER_INPUT_NONE; + break; + } + + if (resizer->input != RESIZER_INPUT_NONE) + return -EBUSY; + + resizer->input = RESIZER_INPUT_PREVIEWER; + set_resizer_channel_cont_mode(resizer); + resizer_chained = 1; + break; + case RESIZER_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Read from memory - single shot mode*/ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + reset_resizer_channel_mode(resizer); + resizer->input = RESIZER_INPUT_NONE; + break; + } + + if (set_resizer_channel_ss_mode(resizer)) + return -EINVAL; + + resizer->input = RESIZER_INPUT_MEMORY; + resizer_chained = 0; + break; + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + resizer->output = RESIZER_OUPUT_MEMORY; + else + resizer->output = RESIZER_OUTPUT_NONE; + + break; + case RESIZER_PAD_SOURCE2 | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + resizer->output2 = RESIZER_OUPUT_MEMORY; + else + resizer->output2 = RESIZER_OUTPUT_NONE; + + break; + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities() - RESIZER subdevs/video + * driver unregistrations. + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->video_in); + vpfe_video_unregister(&vpfe_rsz->video_out); + vpfe_video_unregister(&vpfe_rsz->video_out2); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->subdev); +} + +/* + * vpfe_resizer_register_entities() - RESIZER subdevs/video + * driver registrations. + * @resizer - pointer to resizer subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->subdev); + if (ret < 0) { + pr_err("Failed to register resizer as v4l2-subdev\n"); + return ret; + } + + ret = vpfe_video_register(&resizer->video_in, vdev); + if (ret) { + pr_err("Failed to register RSZ video-in device\n"); + goto out_video_in_register; + } + + resizer->video_in.vpfe_dev = vpfe_dev; + + ret = vpfe_video_register(&resizer->video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ video-out device\n"); + goto out_video_out_register; + } + + resizer->video_out.vpfe_dev = vpfe_dev; + + ret = vpfe_video_register(&resizer->video_out2, vdev); + if (ret) { + pr_err("Failed to register RSZ video-out2 device\n"); + goto out_video_out2_register; + } + + resizer->video_out2.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&resizer->video_in.video_dev.entity, 0, + &resizer->subdev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + ret = media_entity_create_link(&resizer->subdev.entity, 1, + &resizer->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + ret = media_entity_create_link(&resizer->subdev.entity, 2, + &resizer->video_out2.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->video_out2); +out_video_out2_register: + vpfe_video_unregister(&resizer->video_out); +out_video_out_register: + vpfe_video_unregister(&resizer->video_in); +out_video_in_register: + media_entity_cleanup(&resizer->subdev.entity); + v4l2_device_unregister_subdev(&resizer->subdev); + return ret; +} + +/* + * vpfe_resizer_init() - RESIZER module initilaization. + * @vpfe_rsz - pointer to resizer subdevice structure. + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct imp_logical_channel *channel = &vpfe_rsz->channel; + struct v4l2_subdev *resizer = &vpfe_rsz->subdev; + struct media_pad *pads = &vpfe_rsz->pads[0]; + struct media_entity *me = &resizer->entity; + int ret; + + vpfe_rsz->imp_hw_if = imp_get_hw_if(); + if (!vpfe_rsz->imp_hw_if) + return -EINVAL; + + vpfe_rsz->video_in.ops = &video_in_ops; + vpfe_rsz->video_out.ops = &video_out1_ops; + vpfe_rsz->video_out2.ops = &video_out2_ops; + + v4l2_subdev_init(resizer, &resizer_v4l2_ops); + resizer->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(resizer->name, "DAVINCI RESIZER", sizeof(resizer->name)); + resizer->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(resizer, vpfe_rsz); + resizer->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[RESIZER_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->input = RESIZER_INPUT_NONE; + vpfe_rsz->output = RESIZER_OUTPUT_NONE; + vpfe_rsz->output2 = RESIZER_OUTPUT_NONE; + + channel->type = IMP_RESIZER; + channel->config_state = STATE_NOT_CONFIGURED; + + me->ops = &resizer_media_ops; + + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&vpfe_rsz->video_in, "RSZ"); + if (ret) { + pr_err("Failed to init RSZ video-in device\n"); + return ret; + } + + vpfe_rsz->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->video_out, "RSZ"); + if (ret) { + pr_err("Failed to init RSZ video-out device\n"); + return ret; + } + + vpfe_rsz->video_out2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->video_out2, "RSZB"); + if (ret) { + pr_err("Failed to init RSZ video-out2 device\n"); + return ret; + } + + return 0; +} diff --git a/drivers/media/platform/davinci/vpfe_resizer.h b/drivers/media/platform/davinci/vpfe_resizer.h new file mode 100644 index 0000000..819af1f --- /dev/null +++ b/drivers/media/platform/davinci/vpfe_resizer.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * 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 version 2. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _VPFE_RSZ_H +#define _VPFE_RSZ_H + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 +#define RESIZER_PAD_SOURCE2 2 + +#define RESIZER_PADS_NUM 3 + +enum resizer_input_entity { + RESIZER_INPUT_NONE, + RESIZER_INPUT_MEMORY, + RESIZER_INPUT_PREVIEWER, +}; + +#define RESIZER_OUTPUT_NONE 0 +#define RESIZER_OUPUT_MEMORY (1 << 0) + +struct vpfe_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + unsigned int output; + unsigned int output2; + + /* pointer to ipipe function pointers */ + struct imp_hw_interface *imp_hw_if; + struct imp_logical_channel channel; + + struct vpfe_video_device video_in; + struct vpfe_video_device video_out; + struct vpfe_video_device video_out2; +}; + +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void rsz_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_cleanup(struct platform_device *pdev); +void rsz_dma_isr(struct vpfe_resizer_device *resizer); + +#endif