linux/drivers/staging/media/omap4iss/iss_resizer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module
   4 *
   5 * Copyright (C) 2012 Texas Instruments, Inc.
   6 *
   7 * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/uaccess.h>
  12#include <linux/delay.h>
  13#include <linux/device.h>
  14#include <linux/dma-mapping.h>
  15#include <linux/mm.h>
  16#include <linux/sched.h>
  17
  18#include "iss.h"
  19#include "iss_regs.h"
  20#include "iss_resizer.h"
  21
  22static const unsigned int resizer_fmts[] = {
  23        MEDIA_BUS_FMT_UYVY8_1X16,
  24        MEDIA_BUS_FMT_YUYV8_1X16,
  25};
  26
  27/*
  28 * resizer_print_status - Print current RESIZER Module register values.
  29 * @resizer: Pointer to ISS ISP RESIZER device.
  30 *
  31 * Also prints other debug information stored in the RESIZER module.
  32 */
  33#define RSZ_PRINT_REGISTER(iss, name)\
  34        dev_dbg(iss->dev, "###RSZ " #name "=0x%08x\n", \
  35                iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_##name))
  36
  37#define RZA_PRINT_REGISTER(iss, name)\
  38        dev_dbg(iss->dev, "###RZA " #name "=0x%08x\n", \
  39                iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_##name))
  40
  41static void resizer_print_status(struct iss_resizer_device *resizer)
  42{
  43        struct iss_device *iss = to_iss_device(resizer);
  44
  45        dev_dbg(iss->dev, "-------------RESIZER Register dump-------------\n");
  46
  47        RSZ_PRINT_REGISTER(iss, SYSCONFIG);
  48        RSZ_PRINT_REGISTER(iss, IN_FIFO_CTRL);
  49        RSZ_PRINT_REGISTER(iss, FRACDIV);
  50        RSZ_PRINT_REGISTER(iss, SRC_EN);
  51        RSZ_PRINT_REGISTER(iss, SRC_MODE);
  52        RSZ_PRINT_REGISTER(iss, SRC_FMT0);
  53        RSZ_PRINT_REGISTER(iss, SRC_FMT1);
  54        RSZ_PRINT_REGISTER(iss, SRC_VPS);
  55        RSZ_PRINT_REGISTER(iss, SRC_VSZ);
  56        RSZ_PRINT_REGISTER(iss, SRC_HPS);
  57        RSZ_PRINT_REGISTER(iss, SRC_HSZ);
  58        RSZ_PRINT_REGISTER(iss, DMA_RZA);
  59        RSZ_PRINT_REGISTER(iss, DMA_RZB);
  60        RSZ_PRINT_REGISTER(iss, DMA_STA);
  61        RSZ_PRINT_REGISTER(iss, GCK_MMR);
  62        RSZ_PRINT_REGISTER(iss, GCK_SDR);
  63        RSZ_PRINT_REGISTER(iss, IRQ_RZA);
  64        RSZ_PRINT_REGISTER(iss, IRQ_RZB);
  65        RSZ_PRINT_REGISTER(iss, YUV_Y_MIN);
  66        RSZ_PRINT_REGISTER(iss, YUV_Y_MAX);
  67        RSZ_PRINT_REGISTER(iss, YUV_C_MIN);
  68        RSZ_PRINT_REGISTER(iss, YUV_C_MAX);
  69        RSZ_PRINT_REGISTER(iss, SEQ);
  70
  71        RZA_PRINT_REGISTER(iss, EN);
  72        RZA_PRINT_REGISTER(iss, MODE);
  73        RZA_PRINT_REGISTER(iss, 420);
  74        RZA_PRINT_REGISTER(iss, I_VPS);
  75        RZA_PRINT_REGISTER(iss, I_HPS);
  76        RZA_PRINT_REGISTER(iss, O_VSZ);
  77        RZA_PRINT_REGISTER(iss, O_HSZ);
  78        RZA_PRINT_REGISTER(iss, V_PHS_Y);
  79        RZA_PRINT_REGISTER(iss, V_PHS_C);
  80        RZA_PRINT_REGISTER(iss, V_DIF);
  81        RZA_PRINT_REGISTER(iss, V_TYP);
  82        RZA_PRINT_REGISTER(iss, V_LPF);
  83        RZA_PRINT_REGISTER(iss, H_PHS);
  84        RZA_PRINT_REGISTER(iss, H_DIF);
  85        RZA_PRINT_REGISTER(iss, H_TYP);
  86        RZA_PRINT_REGISTER(iss, H_LPF);
  87        RZA_PRINT_REGISTER(iss, DWN_EN);
  88        RZA_PRINT_REGISTER(iss, SDR_Y_BAD_H);
  89        RZA_PRINT_REGISTER(iss, SDR_Y_BAD_L);
  90        RZA_PRINT_REGISTER(iss, SDR_Y_SAD_H);
  91        RZA_PRINT_REGISTER(iss, SDR_Y_SAD_L);
  92        RZA_PRINT_REGISTER(iss, SDR_Y_OFT);
  93        RZA_PRINT_REGISTER(iss, SDR_Y_PTR_S);
  94        RZA_PRINT_REGISTER(iss, SDR_Y_PTR_E);
  95        RZA_PRINT_REGISTER(iss, SDR_C_BAD_H);
  96        RZA_PRINT_REGISTER(iss, SDR_C_BAD_L);
  97        RZA_PRINT_REGISTER(iss, SDR_C_SAD_H);
  98        RZA_PRINT_REGISTER(iss, SDR_C_SAD_L);
  99        RZA_PRINT_REGISTER(iss, SDR_C_OFT);
 100        RZA_PRINT_REGISTER(iss, SDR_C_PTR_S);
 101        RZA_PRINT_REGISTER(iss, SDR_C_PTR_E);
 102
 103        dev_dbg(iss->dev, "-----------------------------------------------\n");
 104}
 105
 106/*
 107 * resizer_enable - Enable/Disable RESIZER.
 108 * @enable: enable flag
 109 *
 110 */
 111static void resizer_enable(struct iss_resizer_device *resizer, u8 enable)
 112{
 113        struct iss_device *iss = to_iss_device(resizer);
 114
 115        iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_EN,
 116                       RSZ_SRC_EN_SRC_EN, enable ? RSZ_SRC_EN_SRC_EN : 0);
 117
 118        /* TODO: Enable RSZB */
 119        iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN,
 120                       enable ? RSZ_EN_EN : 0);
 121}
 122
 123/* -----------------------------------------------------------------------------
 124 * Format- and pipeline-related configuration helpers
 125 */
 126
 127/*
 128 * resizer_set_outaddr - Set memory address to save output image
 129 * @resizer: Pointer to ISP RESIZER device.
 130 * @addr: 32-bit memory address aligned on 32 byte boundary.
 131 *
 132 * Sets the memory address where the output will be saved.
 133 */
 134static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr)
 135{
 136        struct iss_device *iss = to_iss_device(resizer);
 137        struct v4l2_mbus_framefmt *informat, *outformat;
 138
 139        informat = &resizer->formats[RESIZER_PAD_SINK];
 140        outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
 141
 142        /* Save address split in Base Address H & L */
 143        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_H,
 144                      (addr >> 16) & 0xffff);
 145        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_L,
 146                      addr & 0xffff);
 147
 148        /* SAD = BAD */
 149        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_H,
 150                      (addr >> 16) & 0xffff);
 151        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_L,
 152                      addr & 0xffff);
 153
 154        /* Program UV buffer address... Hardcoded to be contiguous! */
 155        if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
 156            (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
 157                u32 c_addr = addr + resizer->video_out.bpl_value
 158                           * outformat->height;
 159
 160                /* Ensure Y_BAD_L[6:0] = C_BAD_L[6:0]*/
 161                if ((c_addr ^ addr) & 0x7f) {
 162                        c_addr &= ~0x7f;
 163                        c_addr += 0x80;
 164                        c_addr |= addr & 0x7f;
 165                }
 166
 167                /* Save address split in Base Address H & L */
 168                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_H,
 169                              (c_addr >> 16) & 0xffff);
 170                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_L,
 171                              c_addr & 0xffff);
 172
 173                /* SAD = BAD */
 174                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_H,
 175                              (c_addr >> 16) & 0xffff);
 176                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_L,
 177                              c_addr & 0xffff);
 178        }
 179}
 180
 181static void resizer_configure(struct iss_resizer_device *resizer)
 182{
 183        struct iss_device *iss = to_iss_device(resizer);
 184        struct v4l2_mbus_framefmt *informat, *outformat;
 185
 186        informat = &resizer->formats[RESIZER_PAD_SINK];
 187        outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
 188
 189        /* Disable pass-through more. Despite its name, the BYPASS bit controls
 190         * pass-through mode, not bypass mode.
 191         */
 192        iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
 193                    RSZ_SRC_FMT0_BYPASS);
 194
 195        /* Select RSZ input */
 196        iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
 197                       RSZ_SRC_FMT0_SEL,
 198                       resizer->input == RESIZER_INPUT_IPIPEIF ?
 199                       RSZ_SRC_FMT0_SEL : 0);
 200
 201        /* RSZ ignores WEN signal from IPIPE/IPIPEIF */
 202        iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
 203                    RSZ_SRC_MODE_WRT);
 204
 205        /* Set Resizer in free-running mode */
 206        iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
 207                    RSZ_SRC_MODE_OST);
 208
 209        /* Init Resizer A */
 210        iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_MODE,
 211                    RZA_MODE_ONE_SHOT);
 212
 213        /* Set size related things now */
 214        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VPS, 0);
 215        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HPS, 0);
 216        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VSZ,
 217                      informat->height - 2);
 218        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HSZ,
 219                      informat->width - 1);
 220
 221        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_VPS, 0);
 222        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_HPS, 0);
 223
 224        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_VSZ,
 225                      outformat->height - 2);
 226        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_HSZ,
 227                      outformat->width - 1);
 228
 229        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_V_DIF, 0x100);
 230        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_H_DIF, 0x100);
 231
 232        /* Buffer output settings */
 233        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_S, 0);
 234        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_E,
 235                      outformat->height - 1);
 236
 237        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_OFT,
 238                      resizer->video_out.bpl_value);
 239
 240        /* UYVY -> NV12 conversion */
 241        if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
 242            (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
 243                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420,
 244                              RSZ_420_CEN | RSZ_420_YEN);
 245
 246                /* UV Buffer output settings */
 247                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_S,
 248                              0);
 249                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_E,
 250                              outformat->height - 1);
 251
 252                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_OFT,
 253                              resizer->video_out.bpl_value);
 254        } else {
 255                iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, 0);
 256        }
 257}
 258
 259/* -----------------------------------------------------------------------------
 260 * Interrupt handling
 261 */
 262
 263static void resizer_isr_buffer(struct iss_resizer_device *resizer)
 264{
 265        struct iss_buffer *buffer;
 266
 267        /* The whole resizer needs to be stopped. Disabling RZA only produces
 268         * input FIFO overflows, most probably when the next frame is received.
 269         */
 270        resizer_enable(resizer, 0);
 271
 272        buffer = omap4iss_video_buffer_next(&resizer->video_out);
 273        if (!buffer)
 274                return;
 275
 276        resizer_set_outaddr(resizer, buffer->iss_addr);
 277
 278        resizer_enable(resizer, 1);
 279}
 280
 281/*
 282 * omap4iss_resizer_isr - Configure resizer during interframe time.
 283 * @resizer: Pointer to ISP RESIZER device.
 284 * @events: RESIZER events
 285 */
 286void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events)
 287{
 288        struct iss_device *iss = to_iss_device(resizer);
 289        struct iss_pipeline *pipe =
 290                             to_iss_pipeline(&resizer->subdev.entity);
 291
 292        if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR |
 293                      ISP5_IRQ_RSZ_FIFO_OVF)) {
 294                dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n",
 295                        events & ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR ? 1 : 0,
 296                        events & ISP5_IRQ_RSZ_FIFO_OVF ? 1 : 0);
 297                omap4iss_pipeline_cancel_stream(pipe);
 298        }
 299
 300        if (omap4iss_module_sync_is_stopping(&resizer->wait,
 301                                             &resizer->stopping))
 302                return;
 303
 304        if (events & ISP5_IRQ_RSZ_INT_DMA)
 305                resizer_isr_buffer(resizer);
 306}
 307
 308/* -----------------------------------------------------------------------------
 309 * ISS video operations
 310 */
 311
 312static int resizer_video_queue(struct iss_video *video,
 313                               struct iss_buffer *buffer)
 314{
 315        struct iss_resizer_device *resizer = container_of(video,
 316                                struct iss_resizer_device, video_out);
 317
 318        if (!(resizer->output & RESIZER_OUTPUT_MEMORY))
 319                return -ENODEV;
 320
 321        resizer_set_outaddr(resizer, buffer->iss_addr);
 322
 323        /*
 324         * If streaming was enabled before there was a buffer queued
 325         * or underrun happened in the ISR, the hardware was not enabled
 326         * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
 327         * Enable it now.
 328         */
 329        if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
 330                resizer_enable(resizer, 1);
 331                iss_video_dmaqueue_flags_clr(video);
 332        }
 333
 334        return 0;
 335}
 336
 337static const struct iss_video_operations resizer_video_ops = {
 338        .queue = resizer_video_queue,
 339};
 340
 341/* -----------------------------------------------------------------------------
 342 * V4L2 subdev operations
 343 */
 344
 345/*
 346 * resizer_set_stream - Enable/Disable streaming on the RESIZER module
 347 * @sd: ISP RESIZER V4L2 subdevice
 348 * @enable: Enable/disable stream
 349 */
 350static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
 351{
 352        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 353        struct iss_device *iss = to_iss_device(resizer);
 354        struct iss_video *video_out = &resizer->video_out;
 355        int ret = 0;
 356
 357        if (resizer->state == ISS_PIPELINE_STREAM_STOPPED) {
 358                if (enable == ISS_PIPELINE_STREAM_STOPPED)
 359                        return 0;
 360
 361                omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
 362
 363                iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
 364                            RSZ_GCK_MMR_MMR);
 365                iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
 366                            RSZ_GCK_SDR_CORE);
 367
 368                /* FIXME: Enable RSZB also */
 369                iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
 370                            RSZ_SYSCONFIG_RSZA_CLK_EN);
 371        }
 372
 373        switch (enable) {
 374        case ISS_PIPELINE_STREAM_CONTINUOUS:
 375
 376                resizer_configure(resizer);
 377                resizer_print_status(resizer);
 378
 379                /*
 380                 * When outputting to memory with no buffer available, let the
 381                 * buffer queue handler start the hardware. A DMA queue flag
 382                 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
 383                 * a buffer available.
 384                 */
 385                if (resizer->output & RESIZER_OUTPUT_MEMORY &&
 386                    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
 387                        break;
 388
 389                atomic_set(&resizer->stopping, 0);
 390                resizer_enable(resizer, 1);
 391                iss_video_dmaqueue_flags_clr(video_out);
 392                break;
 393
 394        case ISS_PIPELINE_STREAM_STOPPED:
 395                if (resizer->state == ISS_PIPELINE_STREAM_STOPPED)
 396                        return 0;
 397                if (omap4iss_module_sync_idle(&sd->entity, &resizer->wait,
 398                                              &resizer->stopping))
 399                        ret = -ETIMEDOUT;
 400
 401                resizer_enable(resizer, 0);
 402                iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
 403                            RSZ_SYSCONFIG_RSZA_CLK_EN);
 404                iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
 405                            RSZ_GCK_SDR_CORE);
 406                iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
 407                            RSZ_GCK_MMR_MMR);
 408                omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
 409                iss_video_dmaqueue_flags_clr(video_out);
 410                break;
 411        }
 412
 413        resizer->state = enable;
 414        return ret;
 415}
 416
 417static struct v4l2_mbus_framefmt *
 418__resizer_get_format(struct iss_resizer_device *resizer,
 419                     struct v4l2_subdev_pad_config *cfg, unsigned int pad,
 420                     enum v4l2_subdev_format_whence which)
 421{
 422        if (which == V4L2_SUBDEV_FORMAT_TRY)
 423                return v4l2_subdev_get_try_format(&resizer->subdev, cfg, pad);
 424        return &resizer->formats[pad];
 425}
 426
 427/*
 428 * resizer_try_format - Try video format on a pad
 429 * @resizer: ISS RESIZER device
 430 * @cfg: V4L2 subdev pad config
 431 * @pad: Pad number
 432 * @fmt: Format
 433 */
 434static void
 435resizer_try_format(struct iss_resizer_device *resizer,
 436                   struct v4l2_subdev_pad_config *cfg, unsigned int pad,
 437                   struct v4l2_mbus_framefmt *fmt,
 438                   enum v4l2_subdev_format_whence which)
 439{
 440        u32 pixelcode;
 441        struct v4l2_mbus_framefmt *format;
 442        unsigned int width = fmt->width;
 443        unsigned int height = fmt->height;
 444        unsigned int i;
 445
 446        switch (pad) {
 447        case RESIZER_PAD_SINK:
 448                for (i = 0; i < ARRAY_SIZE(resizer_fmts); i++) {
 449                        if (fmt->code == resizer_fmts[i])
 450                                break;
 451                }
 452
 453                /* If not found, use UYVY as default */
 454                if (i >= ARRAY_SIZE(resizer_fmts))
 455                        fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
 456
 457                /* Clamp the input size. */
 458                fmt->width = clamp_t(u32, width, 1, 8192);
 459                fmt->height = clamp_t(u32, height, 1, 8192);
 460                break;
 461
 462        case RESIZER_PAD_SOURCE_MEM:
 463                pixelcode = fmt->code;
 464                format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK,
 465                                              which);
 466                memcpy(fmt, format, sizeof(*fmt));
 467
 468                if ((pixelcode == MEDIA_BUS_FMT_YUYV8_1_5X8) &&
 469                    (fmt->code == MEDIA_BUS_FMT_UYVY8_1X16))
 470                        fmt->code = pixelcode;
 471
 472                /* The data formatter truncates the number of horizontal output
 473                 * pixels to a multiple of 16. To avoid clipping data, allow
 474                 * callers to request an output size bigger than the input size
 475                 * up to the nearest multiple of 16.
 476                 */
 477                fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
 478                fmt->width &= ~15;
 479                fmt->height = clamp_t(u32, height, 32, fmt->height);
 480                break;
 481        }
 482
 483        fmt->colorspace = V4L2_COLORSPACE_JPEG;
 484        fmt->field = V4L2_FIELD_NONE;
 485}
 486
 487/*
 488 * resizer_enum_mbus_code - Handle pixel format enumeration
 489 * @sd     : pointer to v4l2 subdev structure
 490 * @cfg: V4L2 subdev pad config
 491 * @code   : pointer to v4l2_subdev_mbus_code_enum structure
 492 * return -EINVAL or zero on success
 493 */
 494static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
 495                                  struct v4l2_subdev_pad_config *cfg,
 496                                  struct v4l2_subdev_mbus_code_enum *code)
 497{
 498        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 499        struct v4l2_mbus_framefmt *format;
 500
 501        switch (code->pad) {
 502        case RESIZER_PAD_SINK:
 503                if (code->index >= ARRAY_SIZE(resizer_fmts))
 504                        return -EINVAL;
 505
 506                code->code = resizer_fmts[code->index];
 507                break;
 508
 509        case RESIZER_PAD_SOURCE_MEM:
 510                format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK,
 511                                              code->which);
 512
 513                if (code->index == 0) {
 514                        code->code = format->code;
 515                        break;
 516                }
 517
 518                switch (format->code) {
 519                case MEDIA_BUS_FMT_UYVY8_1X16:
 520                        if (code->index == 1)
 521                                code->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
 522                        else
 523                                return -EINVAL;
 524                        break;
 525                default:
 526                        if (code->index != 0)
 527                                return -EINVAL;
 528                }
 529
 530                break;
 531
 532        default:
 533                return -EINVAL;
 534        }
 535
 536        return 0;
 537}
 538
 539static int resizer_enum_frame_size(struct v4l2_subdev *sd,
 540                                   struct v4l2_subdev_pad_config *cfg,
 541                                   struct v4l2_subdev_frame_size_enum *fse)
 542{
 543        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 544        struct v4l2_mbus_framefmt format;
 545
 546        if (fse->index != 0)
 547                return -EINVAL;
 548
 549        format.code = fse->code;
 550        format.width = 1;
 551        format.height = 1;
 552        resizer_try_format(resizer, cfg, fse->pad, &format, fse->which);
 553        fse->min_width = format.width;
 554        fse->min_height = format.height;
 555
 556        if (format.code != fse->code)
 557                return -EINVAL;
 558
 559        format.code = fse->code;
 560        format.width = -1;
 561        format.height = -1;
 562        resizer_try_format(resizer, cfg, fse->pad, &format, fse->which);
 563        fse->max_width = format.width;
 564        fse->max_height = format.height;
 565
 566        return 0;
 567}
 568
 569/*
 570 * resizer_get_format - Retrieve the video format on a pad
 571 * @sd : ISP RESIZER V4L2 subdevice
 572 * @cfg: V4L2 subdev pad config
 573 * @fmt: Format
 574 *
 575 * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
 576 * to the format type.
 577 */
 578static int resizer_get_format(struct v4l2_subdev *sd,
 579                              struct v4l2_subdev_pad_config *cfg,
 580                              struct v4l2_subdev_format *fmt)
 581{
 582        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 583        struct v4l2_mbus_framefmt *format;
 584
 585        format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which);
 586        if (!format)
 587                return -EINVAL;
 588
 589        fmt->format = *format;
 590        return 0;
 591}
 592
 593/*
 594 * resizer_set_format - Set the video format on a pad
 595 * @sd : ISP RESIZER V4L2 subdevice
 596 * @cfg: V4L2 subdev pad config
 597 * @fmt: Format
 598 *
 599 * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
 600 * to the format type.
 601 */
 602static int resizer_set_format(struct v4l2_subdev *sd,
 603                              struct v4l2_subdev_pad_config *cfg,
 604                              struct v4l2_subdev_format *fmt)
 605{
 606        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 607        struct v4l2_mbus_framefmt *format;
 608
 609        format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which);
 610        if (!format)
 611                return -EINVAL;
 612
 613        resizer_try_format(resizer, cfg, fmt->pad, &fmt->format, fmt->which);
 614        *format = fmt->format;
 615
 616        /* Propagate the format from sink to source */
 617        if (fmt->pad == RESIZER_PAD_SINK) {
 618                format = __resizer_get_format(resizer, cfg,
 619                                              RESIZER_PAD_SOURCE_MEM,
 620                                              fmt->which);
 621                *format = fmt->format;
 622                resizer_try_format(resizer, cfg, RESIZER_PAD_SOURCE_MEM, format,
 623                                   fmt->which);
 624        }
 625
 626        return 0;
 627}
 628
 629static int resizer_link_validate(struct v4l2_subdev *sd,
 630                                 struct media_link *link,
 631                                 struct v4l2_subdev_format *source_fmt,
 632                                 struct v4l2_subdev_format *sink_fmt)
 633{
 634        /* Check if the two ends match */
 635        if (source_fmt->format.width != sink_fmt->format.width ||
 636            source_fmt->format.height != sink_fmt->format.height)
 637                return -EPIPE;
 638
 639        if (source_fmt->format.code != sink_fmt->format.code)
 640                return -EPIPE;
 641
 642        return 0;
 643}
 644
 645/*
 646 * resizer_init_formats - Initialize formats on all pads
 647 * @sd: ISP RESIZER V4L2 subdevice
 648 * @fh: V4L2 subdev file handle
 649 *
 650 * Initialize all pad formats with default values. If fh is not NULL, try
 651 * formats are initialized on the file handle. Otherwise active formats are
 652 * initialized on the device.
 653 */
 654static int resizer_init_formats(struct v4l2_subdev *sd,
 655                                struct v4l2_subdev_fh *fh)
 656{
 657        struct v4l2_subdev_format format;
 658
 659        memset(&format, 0, sizeof(format));
 660        format.pad = RESIZER_PAD_SINK;
 661        format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
 662        format.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
 663        format.format.width = 4096;
 664        format.format.height = 4096;
 665        resizer_set_format(sd, fh ? fh->pad : NULL, &format);
 666
 667        return 0;
 668}
 669
 670/* V4L2 subdev video operations */
 671static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
 672        .s_stream = resizer_set_stream,
 673};
 674
 675/* V4L2 subdev pad operations */
 676static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
 677        .enum_mbus_code = resizer_enum_mbus_code,
 678        .enum_frame_size = resizer_enum_frame_size,
 679        .get_fmt = resizer_get_format,
 680        .set_fmt = resizer_set_format,
 681        .link_validate = resizer_link_validate,
 682};
 683
 684/* V4L2 subdev operations */
 685static const struct v4l2_subdev_ops resizer_v4l2_ops = {
 686        .video = &resizer_v4l2_video_ops,
 687        .pad = &resizer_v4l2_pad_ops,
 688};
 689
 690/* V4L2 subdev internal operations */
 691static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
 692        .open = resizer_init_formats,
 693};
 694
 695/* -----------------------------------------------------------------------------
 696 * Media entity operations
 697 */
 698
 699/*
 700 * resizer_link_setup - Setup RESIZER connections
 701 * @entity: RESIZER media entity
 702 * @local: Pad at the local end of the link
 703 * @remote: Pad at the remote end of the link
 704 * @flags: Link flags
 705 *
 706 * return -EINVAL or zero on success
 707 */
 708static int resizer_link_setup(struct media_entity *entity,
 709                              const struct media_pad *local,
 710                              const struct media_pad *remote, u32 flags)
 711{
 712        struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
 713        struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
 714        struct iss_device *iss = to_iss_device(resizer);
 715        unsigned int index = local->index;
 716
 717        /* FIXME: this is actually a hack! */
 718        if (is_media_entity_v4l2_subdev(remote->entity))
 719                index |= 2 << 16;
 720
 721        switch (index) {
 722        case RESIZER_PAD_SINK | 2 << 16:
 723                /* Read from IPIPE or IPIPEIF. */
 724                if (!(flags & MEDIA_LNK_FL_ENABLED)) {
 725                        resizer->input = RESIZER_INPUT_NONE;
 726                        break;
 727                }
 728
 729                if (resizer->input != RESIZER_INPUT_NONE)
 730                        return -EBUSY;
 731
 732                if (remote->entity == &iss->ipipeif.subdev.entity)
 733                        resizer->input = RESIZER_INPUT_IPIPEIF;
 734                else if (remote->entity == &iss->ipipe.subdev.entity)
 735                        resizer->input = RESIZER_INPUT_IPIPE;
 736
 737                break;
 738
 739        case RESIZER_PAD_SOURCE_MEM:
 740                /* Write to memory */
 741                if (flags & MEDIA_LNK_FL_ENABLED) {
 742                        if (resizer->output & ~RESIZER_OUTPUT_MEMORY)
 743                                return -EBUSY;
 744                        resizer->output |= RESIZER_OUTPUT_MEMORY;
 745                } else {
 746                        resizer->output &= ~RESIZER_OUTPUT_MEMORY;
 747                }
 748                break;
 749
 750        default:
 751                return -EINVAL;
 752        }
 753
 754        return 0;
 755}
 756
 757/* media operations */
 758static const struct media_entity_operations resizer_media_ops = {
 759        .link_setup = resizer_link_setup,
 760        .link_validate = v4l2_subdev_link_validate,
 761};
 762
 763/*
 764 * resizer_init_entities - Initialize V4L2 subdev and media entity
 765 * @resizer: ISS ISP RESIZER module
 766 *
 767 * Return 0 on success and a negative error code on failure.
 768 */
 769static int resizer_init_entities(struct iss_resizer_device *resizer)
 770{
 771        struct v4l2_subdev *sd = &resizer->subdev;
 772        struct media_pad *pads = resizer->pads;
 773        struct media_entity *me = &sd->entity;
 774        int ret;
 775
 776        resizer->input = RESIZER_INPUT_NONE;
 777
 778        v4l2_subdev_init(sd, &resizer_v4l2_ops);
 779        sd->internal_ops = &resizer_v4l2_internal_ops;
 780        strscpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name));
 781        sd->grp_id = BIT(16);   /* group ID for iss subdevs */
 782        v4l2_set_subdevdata(sd, resizer);
 783        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 784
 785        pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 786        pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE;
 787
 788        me->ops = &resizer_media_ops;
 789        ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
 790        if (ret < 0)
 791                return ret;
 792
 793        resizer_init_formats(sd, NULL);
 794
 795        resizer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 796        resizer->video_out.ops = &resizer_video_ops;
 797        resizer->video_out.iss = to_iss_device(resizer);
 798        resizer->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
 799        resizer->video_out.bpl_alignment = 32;
 800        resizer->video_out.bpl_zero_padding = 1;
 801        resizer->video_out.bpl_max = 0x1ffe0;
 802
 803        return omap4iss_video_init(&resizer->video_out, "ISP resizer a");
 804}
 805
 806void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer)
 807{
 808        v4l2_device_unregister_subdev(&resizer->subdev);
 809        omap4iss_video_unregister(&resizer->video_out);
 810}
 811
 812int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
 813                                       struct v4l2_device *vdev)
 814{
 815        int ret;
 816
 817        /* Register the subdev and video node. */
 818        ret = v4l2_device_register_subdev(vdev, &resizer->subdev);
 819        if (ret < 0)
 820                goto error;
 821
 822        ret = omap4iss_video_register(&resizer->video_out, vdev);
 823        if (ret < 0)
 824                goto error;
 825
 826        return 0;
 827
 828error:
 829        omap4iss_resizer_unregister_entities(resizer);
 830        return ret;
 831}
 832
 833/* -----------------------------------------------------------------------------
 834 * ISP RESIZER initialisation and cleanup
 835 */
 836
 837/*
 838 * omap4iss_resizer_init - RESIZER module initialization.
 839 * @iss: Device pointer specific to the OMAP4 ISS.
 840 *
 841 * TODO: Get the initialisation values from platform data.
 842 *
 843 * Return 0 on success or a negative error code otherwise.
 844 */
 845int omap4iss_resizer_init(struct iss_device *iss)
 846{
 847        struct iss_resizer_device *resizer = &iss->resizer;
 848
 849        resizer->state = ISS_PIPELINE_STREAM_STOPPED;
 850        init_waitqueue_head(&resizer->wait);
 851
 852        return resizer_init_entities(resizer);
 853}
 854
 855/*
 856 * omap4iss_resizer_create_links() - RESIZER pads links creation
 857 * @iss: Pointer to ISS device
 858 *
 859 * return negative error code or zero on success
 860 */
 861int omap4iss_resizer_create_links(struct iss_device *iss)
 862{
 863        struct iss_resizer_device *resizer = &iss->resizer;
 864
 865        /* Connect the RESIZER subdev to the video node. */
 866        return media_create_pad_link(&resizer->subdev.entity,
 867                                     RESIZER_PAD_SOURCE_MEM,
 868                                     &resizer->video_out.video.entity, 0, 0);
 869}
 870
 871/*
 872 * omap4iss_resizer_cleanup - RESIZER module cleanup.
 873 * @iss: Device pointer specific to the OMAP4 ISS.
 874 */
 875void omap4iss_resizer_cleanup(struct iss_device *iss)
 876{
 877        struct iss_resizer_device *resizer = &iss->resizer;
 878
 879        media_entity_cleanup(&resizer->subdev.entity);
 880}
 881