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