1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2021-2022 Bootlin 4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 5 */ 6 7 #include <linux/pm_runtime.h> 8 #include <linux/regmap.h> 9 #include <media/v4l2-device.h> 10 #include <media/v4l2-fwnode.h> 11 12 #include "sun6i_isp.h" 13 #include "sun6i_isp_capture.h" 14 #include "sun6i_isp_params.h" 15 #include "sun6i_isp_proc.h" 16 #include "sun6i_isp_reg.h" 17 18 /* Helpers */ 19 20 void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev, 21 unsigned int *width, unsigned int *height) 22 { 23 if (width) 24 *width = isp_dev->proc.mbus_format.width; 25 if (height) 26 *height = isp_dev->proc.mbus_format.height; 27 } 28 29 /* Format */ 30 31 static const struct sun6i_isp_proc_format sun6i_isp_proc_formats[] = { 32 { 33 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 34 .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, 35 }, 36 { 37 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, 38 .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, 39 }, 40 { 41 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 42 .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, 43 }, 44 { 45 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, 46 .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, 47 }, 48 49 { 50 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 51 .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, 52 }, 53 { 54 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 55 .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, 56 }, 57 { 58 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 59 .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, 60 }, 61 { 62 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 63 .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, 64 }, 65 }; 66 67 const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code) 68 { 69 unsigned int i; 70 71 for (i = 0; i < ARRAY_SIZE(sun6i_isp_proc_formats); i++) 72 if (sun6i_isp_proc_formats[i].mbus_code == mbus_code) 73 return &sun6i_isp_proc_formats[i]; 74 75 return NULL; 76 } 77 78 /* Processor */ 79 80 static void sun6i_isp_proc_irq_enable(struct sun6i_isp_device *isp_dev) 81 { 82 struct regmap *regmap = isp_dev->regmap; 83 84 regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 85 SUN6I_ISP_FE_INT_EN_FINISH | 86 SUN6I_ISP_FE_INT_EN_START | 87 SUN6I_ISP_FE_INT_EN_PARA_SAVE | 88 SUN6I_ISP_FE_INT_EN_PARA_LOAD | 89 SUN6I_ISP_FE_INT_EN_SRC0_FIFO | 90 SUN6I_ISP_FE_INT_EN_ROT_FINISH); 91 } 92 93 static void sun6i_isp_proc_irq_disable(struct sun6i_isp_device *isp_dev) 94 { 95 struct regmap *regmap = isp_dev->regmap; 96 97 regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); 98 } 99 100 static void sun6i_isp_proc_irq_clear(struct sun6i_isp_device *isp_dev) 101 { 102 struct regmap *regmap = isp_dev->regmap; 103 104 regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); 105 regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, 106 SUN6I_ISP_FE_INT_STA_CLEAR); 107 } 108 109 static void sun6i_isp_proc_enable(struct sun6i_isp_device *isp_dev, 110 struct sun6i_isp_proc_source *source) 111 { 112 struct sun6i_isp_proc *proc = &isp_dev->proc; 113 struct regmap *regmap = isp_dev->regmap; 114 u8 mode; 115 116 /* Frontend */ 117 118 if (source == &proc->source_csi0) 119 mode = SUN6I_ISP_SRC_MODE_CSI(0); 120 else 121 mode = SUN6I_ISP_SRC_MODE_CSI(1); 122 123 regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, 124 SUN6I_ISP_FE_CFG_EN | SUN6I_ISP_FE_CFG_SRC0_MODE(mode)); 125 126 regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, 127 SUN6I_ISP_FE_CTRL_VCAP_EN | SUN6I_ISP_FE_CTRL_PARA_READY); 128 } 129 130 static void sun6i_isp_proc_disable(struct sun6i_isp_device *isp_dev) 131 { 132 struct regmap *regmap = isp_dev->regmap; 133 134 /* Frontend */ 135 136 regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, 0); 137 regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, 0); 138 } 139 140 static void sun6i_isp_proc_configure(struct sun6i_isp_device *isp_dev) 141 { 142 struct v4l2_mbus_framefmt *mbus_format = &isp_dev->proc.mbus_format; 143 const struct sun6i_isp_proc_format *format; 144 u32 value; 145 146 /* Module */ 147 148 value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG); 149 value |= SUN6I_ISP_MODULE_EN_SRC0; 150 sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value); 151 152 /* Input */ 153 154 format = sun6i_isp_proc_format_find(mbus_format->code); 155 if (WARN_ON(!format)) 156 return; 157 158 sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODE_REG, 159 SUN6I_ISP_MODE_INPUT_FMT(format->input_format) | 160 SUN6I_ISP_MODE_INPUT_YUV_SEQ(format->input_yuv_seq) | 161 SUN6I_ISP_MODE_SHARP(1) | 162 SUN6I_ISP_MODE_HIST(2)); 163 } 164 165 /* V4L2 Subdev */ 166 167 static int sun6i_isp_proc_s_stream(struct v4l2_subdev *subdev, int on) 168 { 169 struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); 170 struct sun6i_isp_proc *proc = &isp_dev->proc; 171 struct media_pad *local_pad = &proc->pads[SUN6I_ISP_PROC_PAD_SINK_CSI]; 172 struct device *dev = isp_dev->dev; 173 struct sun6i_isp_proc_source *source; 174 struct v4l2_subdev *source_subdev; 175 struct media_pad *remote_pad; 176 int ret; 177 178 /* Source */ 179 180 remote_pad = media_pad_remote_pad_unique(local_pad); 181 if (IS_ERR(remote_pad)) { 182 dev_err(dev, 183 "zero or more than a single source connected to the bridge\n"); 184 return PTR_ERR(remote_pad); 185 } 186 187 source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); 188 189 if (source_subdev == proc->source_csi0.subdev) 190 source = &proc->source_csi0; 191 else 192 source = &proc->source_csi1; 193 194 if (!on) { 195 sun6i_isp_proc_irq_disable(isp_dev); 196 v4l2_subdev_call(source_subdev, video, s_stream, 0); 197 ret = 0; 198 goto disable; 199 } 200 201 /* PM */ 202 203 ret = pm_runtime_resume_and_get(dev); 204 if (ret < 0) 205 return ret; 206 207 /* Clear */ 208 209 sun6i_isp_proc_irq_clear(isp_dev); 210 211 /* Configure */ 212 213 sun6i_isp_tables_configure(isp_dev); 214 sun6i_isp_params_configure(isp_dev); 215 sun6i_isp_proc_configure(isp_dev); 216 sun6i_isp_capture_configure(isp_dev); 217 218 /* State Update */ 219 220 sun6i_isp_state_update(isp_dev, true); 221 222 /* Enable */ 223 224 sun6i_isp_proc_irq_enable(isp_dev); 225 sun6i_isp_proc_enable(isp_dev, source); 226 227 ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); 228 if (ret && ret != -ENOIOCTLCMD) { 229 sun6i_isp_proc_irq_disable(isp_dev); 230 goto disable; 231 } 232 233 return 0; 234 235 disable: 236 sun6i_isp_proc_disable(isp_dev); 237 238 pm_runtime_put(dev); 239 240 return ret; 241 } 242 243 static const struct v4l2_subdev_video_ops sun6i_isp_proc_video_ops = { 244 .s_stream = sun6i_isp_proc_s_stream, 245 }; 246 247 static void 248 sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) 249 { 250 if (!sun6i_isp_proc_format_find(mbus_format->code)) 251 mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; 252 253 mbus_format->field = V4L2_FIELD_NONE; 254 mbus_format->colorspace = V4L2_COLORSPACE_RAW; 255 mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; 256 mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; 257 } 258 259 static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, 260 struct v4l2_subdev_state *state) 261 { 262 struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); 263 unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; 264 struct v4l2_mbus_framefmt *mbus_format = 265 v4l2_subdev_get_try_format(subdev, state, pad); 266 struct mutex *lock = &isp_dev->proc.lock; 267 268 mutex_lock(lock); 269 270 mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; 271 mbus_format->width = 1280; 272 mbus_format->height = 720; 273 274 sun6i_isp_proc_mbus_format_prepare(mbus_format); 275 276 mutex_unlock(lock); 277 278 return 0; 279 } 280 281 static int 282 sun6i_isp_proc_enum_mbus_code(struct v4l2_subdev *subdev, 283 struct v4l2_subdev_state *state, 284 struct v4l2_subdev_mbus_code_enum *code_enum) 285 { 286 if (code_enum->index >= ARRAY_SIZE(sun6i_isp_proc_formats)) 287 return -EINVAL; 288 289 code_enum->code = sun6i_isp_proc_formats[code_enum->index].mbus_code; 290 291 return 0; 292 } 293 294 static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev, 295 struct v4l2_subdev_state *state, 296 struct v4l2_subdev_format *format) 297 { 298 struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); 299 struct v4l2_mbus_framefmt *mbus_format = &format->format; 300 struct mutex *lock = &isp_dev->proc.lock; 301 302 mutex_lock(lock); 303 304 if (format->which == V4L2_SUBDEV_FORMAT_TRY) 305 *mbus_format = *v4l2_subdev_get_try_format(subdev, state, 306 format->pad); 307 else 308 *mbus_format = isp_dev->proc.mbus_format; 309 310 mutex_unlock(lock); 311 312 return 0; 313 } 314 315 static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, 316 struct v4l2_subdev_state *state, 317 struct v4l2_subdev_format *format) 318 { 319 struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); 320 struct v4l2_mbus_framefmt *mbus_format = &format->format; 321 struct mutex *lock = &isp_dev->proc.lock; 322 323 mutex_lock(lock); 324 325 sun6i_isp_proc_mbus_format_prepare(mbus_format); 326 327 if (format->which == V4L2_SUBDEV_FORMAT_TRY) 328 *v4l2_subdev_get_try_format(subdev, state, format->pad) = 329 *mbus_format; 330 else 331 isp_dev->proc.mbus_format = *mbus_format; 332 333 mutex_unlock(lock); 334 335 return 0; 336 } 337 338 static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = { 339 .init_cfg = sun6i_isp_proc_init_cfg, 340 .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, 341 .get_fmt = sun6i_isp_proc_get_fmt, 342 .set_fmt = sun6i_isp_proc_set_fmt, 343 }; 344 345 static const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { 346 .video = &sun6i_isp_proc_video_ops, 347 .pad = &sun6i_isp_proc_pad_ops, 348 }; 349 350 /* Media Entity */ 351 352 static const struct media_entity_operations sun6i_isp_proc_entity_ops = { 353 .link_validate = v4l2_subdev_link_validate, 354 }; 355 356 /* V4L2 Async */ 357 358 static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev, 359 int sink_pad_index, 360 struct v4l2_subdev *remote_subdev, bool enabled) 361 { 362 struct device *dev = isp_dev->dev; 363 struct v4l2_subdev *subdev = &isp_dev->proc.subdev; 364 struct media_entity *sink_entity = &subdev->entity; 365 struct media_entity *source_entity = &remote_subdev->entity; 366 int source_pad_index; 367 int ret; 368 369 /* Get the first remote source pad. */ 370 ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, 371 MEDIA_PAD_FL_SOURCE); 372 if (ret < 0) { 373 dev_err(dev, "missing source pad in external entity %s\n", 374 source_entity->name); 375 return -EINVAL; 376 } 377 378 source_pad_index = ret; 379 380 dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, 381 source_pad_index, sink_entity->name, sink_pad_index); 382 383 ret = media_create_pad_link(source_entity, source_pad_index, 384 sink_entity, sink_pad_index, 385 enabled ? MEDIA_LNK_FL_ENABLED : 0); 386 if (ret < 0) { 387 dev_err(dev, "failed to create %s:%u -> %s:%u link\n", 388 source_entity->name, source_pad_index, 389 sink_entity->name, sink_pad_index); 390 return ret; 391 } 392 393 return 0; 394 } 395 396 static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier, 397 struct v4l2_subdev *remote_subdev, 398 struct v4l2_async_connection *async_subdev) 399 { 400 struct sun6i_isp_device *isp_dev = 401 container_of(notifier, struct sun6i_isp_device, proc.notifier); 402 struct sun6i_isp_proc_async_subdev *proc_async_subdev = 403 container_of(async_subdev, struct sun6i_isp_proc_async_subdev, 404 async_subdev); 405 struct sun6i_isp_proc *proc = &isp_dev->proc; 406 struct sun6i_isp_proc_source *source = proc_async_subdev->source; 407 bool enabled; 408 409 switch (source->endpoint.base.port) { 410 case SUN6I_ISP_PORT_CSI0: 411 source = &proc->source_csi0; 412 enabled = true; 413 break; 414 case SUN6I_ISP_PORT_CSI1: 415 source = &proc->source_csi1; 416 enabled = !proc->source_csi0.expected; 417 break; 418 default: 419 return -EINVAL; 420 } 421 422 source->subdev = remote_subdev; 423 424 return sun6i_isp_proc_link(isp_dev, SUN6I_ISP_PROC_PAD_SINK_CSI, 425 remote_subdev, enabled); 426 } 427 428 static int 429 sun6i_isp_proc_notifier_complete(struct v4l2_async_notifier *notifier) 430 { 431 struct sun6i_isp_device *isp_dev = 432 container_of(notifier, struct sun6i_isp_device, proc.notifier); 433 struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; 434 int ret; 435 436 ret = v4l2_device_register_subdev_nodes(v4l2_dev); 437 if (ret) 438 return ret; 439 440 return 0; 441 } 442 443 static const struct v4l2_async_notifier_operations 444 sun6i_isp_proc_notifier_ops = { 445 .bound = sun6i_isp_proc_notifier_bound, 446 .complete = sun6i_isp_proc_notifier_complete, 447 }; 448 449 /* Processor */ 450 451 static int sun6i_isp_proc_source_setup(struct sun6i_isp_device *isp_dev, 452 struct sun6i_isp_proc_source *source, 453 u32 port) 454 { 455 struct device *dev = isp_dev->dev; 456 struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; 457 struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; 458 struct sun6i_isp_proc_async_subdev *proc_async_subdev; 459 struct fwnode_handle *handle = NULL; 460 int ret; 461 462 handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); 463 if (!handle) 464 return -ENODEV; 465 466 ret = v4l2_fwnode_endpoint_parse(handle, endpoint); 467 if (ret) 468 goto complete; 469 470 proc_async_subdev = 471 v4l2_async_nf_add_fwnode_remote(notifier, handle, 472 struct 473 sun6i_isp_proc_async_subdev); 474 if (IS_ERR(proc_async_subdev)) { 475 ret = PTR_ERR(proc_async_subdev); 476 goto complete; 477 } 478 479 proc_async_subdev->source = source; 480 481 source->expected = true; 482 483 complete: 484 fwnode_handle_put(handle); 485 486 return ret; 487 } 488 489 int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) 490 { 491 struct device *dev = isp_dev->dev; 492 struct sun6i_isp_proc *proc = &isp_dev->proc; 493 struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; 494 struct v4l2_async_notifier *notifier = &proc->notifier; 495 struct v4l2_subdev *subdev = &proc->subdev; 496 struct media_pad *pads = proc->pads; 497 int ret; 498 499 mutex_init(&proc->lock); 500 501 /* V4L2 Subdev */ 502 503 v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); 504 strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); 505 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 506 subdev->owner = THIS_MODULE; 507 subdev->dev = dev; 508 509 v4l2_set_subdevdata(subdev, isp_dev); 510 511 /* Media Entity */ 512 513 subdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; 514 subdev->entity.ops = &sun6i_isp_proc_entity_ops; 515 516 /* Media Pads */ 517 518 pads[SUN6I_ISP_PROC_PAD_SINK_CSI].flags = MEDIA_PAD_FL_SINK | 519 MEDIA_PAD_FL_MUST_CONNECT; 520 pads[SUN6I_ISP_PROC_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK | 521 MEDIA_PAD_FL_MUST_CONNECT; 522 pads[SUN6I_ISP_PROC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 523 524 ret = media_entity_pads_init(&subdev->entity, SUN6I_ISP_PROC_PAD_COUNT, 525 pads); 526 if (ret) 527 return ret; 528 529 /* V4L2 Subdev */ 530 531 ret = v4l2_device_register_subdev(v4l2_dev, subdev); 532 if (ret < 0) { 533 v4l2_err(v4l2_dev, "failed to register v4l2 subdev: %d\n", ret); 534 goto error_media_entity; 535 } 536 537 /* V4L2 Async */ 538 539 v4l2_async_nf_init(notifier, v4l2_dev); 540 notifier->ops = &sun6i_isp_proc_notifier_ops; 541 542 sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0, 543 SUN6I_ISP_PORT_CSI0); 544 sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1, 545 SUN6I_ISP_PORT_CSI1); 546 547 ret = v4l2_async_nf_register(notifier); 548 if (ret) { 549 v4l2_err(v4l2_dev, 550 "failed to register v4l2 async notifier: %d\n", ret); 551 goto error_v4l2_async_notifier; 552 } 553 554 return 0; 555 556 error_v4l2_async_notifier: 557 v4l2_async_nf_cleanup(notifier); 558 559 v4l2_device_unregister_subdev(subdev); 560 561 error_media_entity: 562 media_entity_cleanup(&subdev->entity); 563 564 return ret; 565 } 566 567 void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev) 568 { 569 struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; 570 struct v4l2_subdev *subdev = &isp_dev->proc.subdev; 571 572 v4l2_async_nf_unregister(notifier); 573 v4l2_async_nf_cleanup(notifier); 574 575 v4l2_device_unregister_subdev(subdev); 576 media_entity_cleanup(&subdev->entity); 577 } 578