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_state(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_state_get_format(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_state_get_format(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_state_get_format(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 .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, 340 .get_fmt = sun6i_isp_proc_get_fmt, 341 .set_fmt = sun6i_isp_proc_set_fmt, 342 }; 343 344 static const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { 345 .video = &sun6i_isp_proc_video_ops, 346 .pad = &sun6i_isp_proc_pad_ops, 347 }; 348 349 static const struct v4l2_subdev_internal_ops sun6i_isp_proc_internal_ops = { 350 .init_state = sun6i_isp_proc_init_state, 351 }; 352 353 /* Media Entity */ 354 355 static const struct media_entity_operations sun6i_isp_proc_entity_ops = { 356 .link_validate = v4l2_subdev_link_validate, 357 }; 358 359 /* V4L2 Async */ 360 361 static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev, 362 int sink_pad_index, 363 struct v4l2_subdev *remote_subdev, bool enabled) 364 { 365 struct device *dev = isp_dev->dev; 366 struct v4l2_subdev *subdev = &isp_dev->proc.subdev; 367 struct media_entity *sink_entity = &subdev->entity; 368 struct media_entity *source_entity = &remote_subdev->entity; 369 int source_pad_index; 370 int ret; 371 372 /* Get the first remote source pad. */ 373 ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, 374 MEDIA_PAD_FL_SOURCE); 375 if (ret < 0) { 376 dev_err(dev, "missing source pad in external entity %s\n", 377 source_entity->name); 378 return -EINVAL; 379 } 380 381 source_pad_index = ret; 382 383 dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, 384 source_pad_index, sink_entity->name, sink_pad_index); 385 386 ret = media_create_pad_link(source_entity, source_pad_index, 387 sink_entity, sink_pad_index, 388 enabled ? MEDIA_LNK_FL_ENABLED : 0); 389 if (ret < 0) { 390 dev_err(dev, "failed to create %s:%u -> %s:%u link\n", 391 source_entity->name, source_pad_index, 392 sink_entity->name, sink_pad_index); 393 return ret; 394 } 395 396 return 0; 397 } 398 399 static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier, 400 struct v4l2_subdev *remote_subdev, 401 struct v4l2_async_connection *async_subdev) 402 { 403 struct sun6i_isp_device *isp_dev = 404 container_of(notifier, struct sun6i_isp_device, proc.notifier); 405 struct sun6i_isp_proc_async_subdev *proc_async_subdev = 406 container_of(async_subdev, struct sun6i_isp_proc_async_subdev, 407 async_subdev); 408 struct sun6i_isp_proc *proc = &isp_dev->proc; 409 struct sun6i_isp_proc_source *source = proc_async_subdev->source; 410 bool enabled; 411 412 switch (source->endpoint.base.port) { 413 case SUN6I_ISP_PORT_CSI0: 414 source = &proc->source_csi0; 415 enabled = true; 416 break; 417 case SUN6I_ISP_PORT_CSI1: 418 source = &proc->source_csi1; 419 enabled = !proc->source_csi0.expected; 420 break; 421 default: 422 return -EINVAL; 423 } 424 425 source->subdev = remote_subdev; 426 427 return sun6i_isp_proc_link(isp_dev, SUN6I_ISP_PROC_PAD_SINK_CSI, 428 remote_subdev, enabled); 429 } 430 431 static int 432 sun6i_isp_proc_notifier_complete(struct v4l2_async_notifier *notifier) 433 { 434 struct sun6i_isp_device *isp_dev = 435 container_of(notifier, struct sun6i_isp_device, proc.notifier); 436 struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; 437 int ret; 438 439 ret = v4l2_device_register_subdev_nodes(v4l2_dev); 440 if (ret) 441 return ret; 442 443 return 0; 444 } 445 446 static const struct v4l2_async_notifier_operations 447 sun6i_isp_proc_notifier_ops = { 448 .bound = sun6i_isp_proc_notifier_bound, 449 .complete = sun6i_isp_proc_notifier_complete, 450 }; 451 452 /* Processor */ 453 454 static int sun6i_isp_proc_source_setup(struct sun6i_isp_device *isp_dev, 455 struct sun6i_isp_proc_source *source, 456 u32 port) 457 { 458 struct device *dev = isp_dev->dev; 459 struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; 460 struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; 461 struct sun6i_isp_proc_async_subdev *proc_async_subdev; 462 struct fwnode_handle *handle = NULL; 463 int ret; 464 465 handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); 466 if (!handle) 467 return -ENODEV; 468 469 ret = v4l2_fwnode_endpoint_parse(handle, endpoint); 470 if (ret) 471 goto complete; 472 473 proc_async_subdev = 474 v4l2_async_nf_add_fwnode_remote(notifier, handle, 475 struct 476 sun6i_isp_proc_async_subdev); 477 if (IS_ERR(proc_async_subdev)) { 478 ret = PTR_ERR(proc_async_subdev); 479 goto complete; 480 } 481 482 proc_async_subdev->source = source; 483 484 source->expected = true; 485 486 complete: 487 fwnode_handle_put(handle); 488 489 return ret; 490 } 491 492 int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) 493 { 494 struct device *dev = isp_dev->dev; 495 struct sun6i_isp_proc *proc = &isp_dev->proc; 496 struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; 497 struct v4l2_async_notifier *notifier = &proc->notifier; 498 struct v4l2_subdev *subdev = &proc->subdev; 499 struct media_pad *pads = proc->pads; 500 int ret; 501 502 mutex_init(&proc->lock); 503 504 /* V4L2 Subdev */ 505 506 v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); 507 subdev->internal_ops = &sun6i_isp_proc_internal_ops; 508 strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); 509 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 510 subdev->owner = THIS_MODULE; 511 subdev->dev = dev; 512 513 v4l2_set_subdevdata(subdev, isp_dev); 514 515 /* Media Entity */ 516 517 subdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; 518 subdev->entity.ops = &sun6i_isp_proc_entity_ops; 519 520 /* Media Pads */ 521 522 pads[SUN6I_ISP_PROC_PAD_SINK_CSI].flags = MEDIA_PAD_FL_SINK | 523 MEDIA_PAD_FL_MUST_CONNECT; 524 pads[SUN6I_ISP_PROC_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK | 525 MEDIA_PAD_FL_MUST_CONNECT; 526 pads[SUN6I_ISP_PROC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 527 528 ret = media_entity_pads_init(&subdev->entity, SUN6I_ISP_PROC_PAD_COUNT, 529 pads); 530 if (ret) 531 return ret; 532 533 /* V4L2 Subdev */ 534 535 ret = v4l2_device_register_subdev(v4l2_dev, subdev); 536 if (ret < 0) { 537 v4l2_err(v4l2_dev, "failed to register v4l2 subdev: %d\n", ret); 538 goto error_media_entity; 539 } 540 541 /* V4L2 Async */ 542 543 v4l2_async_nf_init(notifier, v4l2_dev); 544 notifier->ops = &sun6i_isp_proc_notifier_ops; 545 546 sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0, 547 SUN6I_ISP_PORT_CSI0); 548 sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1, 549 SUN6I_ISP_PORT_CSI1); 550 551 ret = v4l2_async_nf_register(notifier); 552 if (ret) { 553 v4l2_err(v4l2_dev, 554 "failed to register v4l2 async notifier: %d\n", ret); 555 goto error_v4l2_async_notifier; 556 } 557 558 return 0; 559 560 error_v4l2_async_notifier: 561 v4l2_async_nf_cleanup(notifier); 562 563 v4l2_device_unregister_subdev(subdev); 564 565 error_media_entity: 566 media_entity_cleanup(&subdev->entity); 567 568 return ret; 569 } 570 571 void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev) 572 { 573 struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; 574 struct v4l2_subdev *subdev = &isp_dev->proc.subdev; 575 576 v4l2_async_nf_unregister(notifier); 577 v4l2_async_nf_cleanup(notifier); 578 579 v4l2_device_unregister_subdev(subdev); 580 media_entity_cleanup(&subdev->entity); 581 } 582