Lines Matching +full:csi +full:- +full:2
1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
10 #include <linux/dma-mapping.h>
30 #define MODULE_NAME "sun6i-csi"
33 struct sun6i_csi csi; member
44 static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi) in sun6i_csi_to_dev() argument
46 return container_of(csi, struct sun6i_csi_dev, csi); in sun6i_csi_to_dev()
50 bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, in sun6i_csi_is_format_supported() argument
53 struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); in sun6i_csi_is_format_supported()
60 if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL in sun6i_csi_is_format_supported()
61 || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656) in sun6i_csi_is_format_supported()
62 && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) { in sun6i_csi_is_format_supported()
79 dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n", in sun6i_csi_is_format_supported()
85 dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", in sun6i_csi_is_format_supported()
142 dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n", in sun6i_csi_is_format_supported()
157 dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); in sun6i_csi_is_format_supported()
164 int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) in sun6i_csi_set_power() argument
166 struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); in sun6i_csi_set_power()
167 struct device *dev = sdev->dev; in sun6i_csi_set_power()
168 struct regmap *regmap = sdev->regmap; in sun6i_csi_set_power()
174 clk_disable_unprepare(sdev->clk_ram); in sun6i_csi_set_power()
175 if (of_device_is_compatible(dev->of_node, in sun6i_csi_set_power()
176 "allwinner,sun50i-a64-csi")) in sun6i_csi_set_power()
177 clk_rate_exclusive_put(sdev->clk_mod); in sun6i_csi_set_power()
178 clk_disable_unprepare(sdev->clk_mod); in sun6i_csi_set_power()
179 reset_control_assert(sdev->rstc_bus); in sun6i_csi_set_power()
183 ret = clk_prepare_enable(sdev->clk_mod); in sun6i_csi_set_power()
185 dev_err(sdev->dev, "Enable csi clk err %d\n", ret); in sun6i_csi_set_power()
189 if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) in sun6i_csi_set_power()
190 clk_set_rate_exclusive(sdev->clk_mod, 300000000); in sun6i_csi_set_power()
192 ret = clk_prepare_enable(sdev->clk_ram); in sun6i_csi_set_power()
194 dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret); in sun6i_csi_set_power()
198 ret = reset_control_deassert(sdev->rstc_bus); in sun6i_csi_set_power()
200 dev_err(sdev->dev, "reset err %d\n", ret); in sun6i_csi_set_power()
209 clk_disable_unprepare(sdev->clk_ram); in sun6i_csi_set_power()
211 if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) in sun6i_csi_set_power()
212 clk_rate_exclusive_put(sdev->clk_mod); in sun6i_csi_set_power()
213 clk_disable_unprepare(sdev->clk_mod); in sun6i_csi_set_power()
220 /* non-YUV */ in get_csi_input_format()
235 dev_dbg(sdev->dev, "Select YUV422 as default input format of CSI.\n"); in get_csi_input_format()
299 dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); in get_csi_output_format()
309 /* Input sequence does not apply to non-YUV formats */ in get_csi_input_seq()
333 dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n", in get_csi_input_seq()
355 dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n", in get_csi_input_seq()
365 dev_warn(sdev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n", in get_csi_input_seq()
375 struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep; in sun6i_csi_setup_bus()
376 struct sun6i_csi *csi = &sdev->csi; in sun6i_csi_setup_bus() local
382 if (csi->config.field == V4L2_FIELD_INTERLACED in sun6i_csi_setup_bus()
383 || csi->config.field == V4L2_FIELD_INTERLACED_TB in sun6i_csi_setup_bus()
384 || csi->config.field == V4L2_FIELD_INTERLACED_BT) in sun6i_csi_setup_bus()
387 bus_width = endpoint->bus.parallel.bus_width; in sun6i_csi_setup_bus()
389 regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg); in sun6i_csi_setup_bus()
402 switch (endpoint->bus_type) { in sun6i_csi_setup_bus()
406 flags = endpoint->bus.parallel.flags; in sun6i_csi_setup_bus()
425 flags = endpoint->bus.parallel.flags; in sun6i_csi_setup_bus()
437 dev_warn(sdev->dev, "Unsupported bus type: %d\n", in sun6i_csi_setup_bus()
438 endpoint->bus_type); in sun6i_csi_setup_bus()
455 dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width); in sun6i_csi_setup_bus()
459 regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg); in sun6i_csi_setup_bus()
464 struct sun6i_csi *csi = &sdev->csi; in sun6i_csi_set_format() local
468 regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg); in sun6i_csi_set_format()
475 val = get_csi_input_format(sdev, csi->config.code, in sun6i_csi_set_format()
476 csi->config.pixelformat); in sun6i_csi_set_format()
479 val = get_csi_output_format(sdev, csi->config.pixelformat, in sun6i_csi_set_format()
480 csi->config.field); in sun6i_csi_set_format()
483 val = get_csi_input_seq(sdev, csi->config.code, in sun6i_csi_set_format()
484 csi->config.pixelformat); in sun6i_csi_set_format()
487 if (csi->config.field == V4L2_FIELD_TOP) in sun6i_csi_set_format()
489 else if (csi->config.field == V4L2_FIELD_BOTTOM) in sun6i_csi_set_format()
494 regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg); in sun6i_csi_set_format()
499 struct sun6i_csi_config *config = &sdev->csi.config; in sun6i_csi_set_window()
502 int *planar_offset = sdev->planar_offset; in sun6i_csi_set_window()
503 u32 width = config->width; in sun6i_csi_set_window()
504 u32 height = config->height; in sun6i_csi_set_window()
507 switch (config->pixelformat) { in sun6i_csi_set_window()
512 dev_dbg(sdev->dev, in sun6i_csi_set_window()
513 "Horizontal length should be 2 times of width for packed YUV formats!\n"); in sun6i_csi_set_window()
514 hor_len = width * 2; in sun6i_csi_set_window()
520 regmap_write(sdev->regmap, CSI_CH_HSIZE_REG, in sun6i_csi_set_window()
523 regmap_write(sdev->regmap, CSI_CH_VSIZE_REG, in sun6i_csi_set_window()
528 switch (config->pixelformat) { in sun6i_csi_set_window()
537 planar_offset[2] = -1; in sun6i_csi_set_window()
542 bytesperline_c = width / 2; in sun6i_csi_set_window()
544 planar_offset[2] = planar_offset[1] + in sun6i_csi_set_window()
545 bytesperline_c * height / 2; in sun6i_csi_set_window()
549 bytesperline_c = width / 2; in sun6i_csi_set_window()
551 planar_offset[2] = planar_offset[1] + in sun6i_csi_set_window()
555 dev_dbg(sdev->dev, in sun6i_csi_set_window()
557 config->pixelformat); in sun6i_csi_set_window()
558 bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) * in sun6i_csi_set_window()
559 config->width) / 8; in sun6i_csi_set_window()
561 planar_offset[1] = -1; in sun6i_csi_set_window()
562 planar_offset[2] = -1; in sun6i_csi_set_window()
566 regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG, in sun6i_csi_set_window()
571 int sun6i_csi_update_config(struct sun6i_csi *csi, in sun6i_csi_update_config() argument
574 struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); in sun6i_csi_update_config()
577 return -EINVAL; in sun6i_csi_update_config()
579 memcpy(&csi->config, config, sizeof(csi->config)); in sun6i_csi_update_config()
588 void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr) in sun6i_csi_update_buf_addr() argument
590 struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); in sun6i_csi_update_buf_addr()
592 regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG, in sun6i_csi_update_buf_addr()
593 (addr + sdev->planar_offset[0]) >> 2); in sun6i_csi_update_buf_addr()
594 if (sdev->planar_offset[1] != -1) in sun6i_csi_update_buf_addr()
595 regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG, in sun6i_csi_update_buf_addr()
596 (addr + sdev->planar_offset[1]) >> 2); in sun6i_csi_update_buf_addr()
597 if (sdev->planar_offset[2] != -1) in sun6i_csi_update_buf_addr()
598 regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG, in sun6i_csi_update_buf_addr()
599 (addr + sdev->planar_offset[2]) >> 2); in sun6i_csi_update_buf_addr()
602 void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable) in sun6i_csi_set_stream() argument
604 struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); in sun6i_csi_set_stream()
605 struct regmap *regmap = sdev->regmap; in sun6i_csi_set_stream()
626 /* -----------------------------------------------------------------------------
629 static int sun6i_csi_link_entity(struct sun6i_csi *csi, in sun6i_csi_link_entity() argument
640 dev_err(csi->dev, "%s: no source pad in external entity %s\n", in sun6i_csi_link_entity()
641 __func__, entity->name); in sun6i_csi_link_entity()
642 return -EINVAL; in sun6i_csi_link_entity()
647 sink = &csi->video.vdev.entity; in sun6i_csi_link_entity()
648 sink_pad = &csi->video.pad; in sun6i_csi_link_entity()
650 dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n", in sun6i_csi_link_entity()
651 entity->name, src_pad_index, sink->name, sink_pad->index); in sun6i_csi_link_entity()
653 sink_pad->index, in sun6i_csi_link_entity()
657 dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n", in sun6i_csi_link_entity()
658 entity->name, src_pad_index, in sun6i_csi_link_entity()
659 sink->name, sink_pad->index); in sun6i_csi_link_entity()
668 struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi, in sun6i_subdev_notify_complete() local
670 struct v4l2_device *v4l2_dev = &csi->v4l2_dev; in sun6i_subdev_notify_complete()
674 dev_dbg(csi->dev, "notify complete, all subdevs registered\n"); in sun6i_subdev_notify_complete()
676 sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list); in sun6i_subdev_notify_complete()
678 return -EINVAL; in sun6i_subdev_notify_complete()
680 ret = sun6i_csi_link_entity(csi, &sd->entity, sd->fwnode); in sun6i_subdev_notify_complete()
684 ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); in sun6i_subdev_notify_complete()
688 return media_device_register(&csi->media_dev); in sun6i_subdev_notify_complete()
699 struct sun6i_csi *csi = dev_get_drvdata(dev); in sun6i_csi_fwnode_parse() local
701 if (vep->base.port || vep->base.id) { in sun6i_csi_fwnode_parse()
703 return -ENOTCONN; in sun6i_csi_fwnode_parse()
706 switch (vep->bus_type) { in sun6i_csi_fwnode_parse()
709 csi->v4l2_ep = *vep; in sun6i_csi_fwnode_parse()
713 return -ENOTCONN; in sun6i_csi_fwnode_parse()
717 static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi) in sun6i_csi_v4l2_cleanup() argument
719 media_device_unregister(&csi->media_dev); in sun6i_csi_v4l2_cleanup()
720 v4l2_async_notifier_unregister(&csi->notifier); in sun6i_csi_v4l2_cleanup()
721 v4l2_async_notifier_cleanup(&csi->notifier); in sun6i_csi_v4l2_cleanup()
722 sun6i_video_cleanup(&csi->video); in sun6i_csi_v4l2_cleanup()
723 v4l2_device_unregister(&csi->v4l2_dev); in sun6i_csi_v4l2_cleanup()
724 v4l2_ctrl_handler_free(&csi->ctrl_handler); in sun6i_csi_v4l2_cleanup()
725 media_device_cleanup(&csi->media_dev); in sun6i_csi_v4l2_cleanup()
728 static int sun6i_csi_v4l2_init(struct sun6i_csi *csi) in sun6i_csi_v4l2_init() argument
732 csi->media_dev.dev = csi->dev; in sun6i_csi_v4l2_init()
733 strscpy(csi->media_dev.model, "Allwinner Video Capture Device", in sun6i_csi_v4l2_init()
734 sizeof(csi->media_dev.model)); in sun6i_csi_v4l2_init()
735 csi->media_dev.hw_revision = 0; in sun6i_csi_v4l2_init()
736 snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info), in sun6i_csi_v4l2_init()
737 "platform:%s", dev_name(csi->dev)); in sun6i_csi_v4l2_init()
739 media_device_init(&csi->media_dev); in sun6i_csi_v4l2_init()
740 v4l2_async_notifier_init(&csi->notifier); in sun6i_csi_v4l2_init()
742 ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0); in sun6i_csi_v4l2_init()
744 dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n", in sun6i_csi_v4l2_init()
749 csi->v4l2_dev.mdev = &csi->media_dev; in sun6i_csi_v4l2_init()
750 csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler; in sun6i_csi_v4l2_init()
751 ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); in sun6i_csi_v4l2_init()
753 dev_err(csi->dev, "V4L2 device registration failed (%d)\n", in sun6i_csi_v4l2_init()
758 ret = sun6i_video_init(&csi->video, csi, "sun6i-csi"); in sun6i_csi_v4l2_init()
762 ret = v4l2_async_notifier_parse_fwnode_endpoints(csi->dev, in sun6i_csi_v4l2_init()
763 &csi->notifier, in sun6i_csi_v4l2_init()
769 csi->notifier.ops = &sun6i_csi_async_ops; in sun6i_csi_v4l2_init()
771 ret = v4l2_async_notifier_register(&csi->v4l2_dev, &csi->notifier); in sun6i_csi_v4l2_init()
773 dev_err(csi->dev, "notifier registration failed\n"); in sun6i_csi_v4l2_init()
780 sun6i_video_cleanup(&csi->video); in sun6i_csi_v4l2_init()
782 v4l2_device_unregister(&csi->v4l2_dev); in sun6i_csi_v4l2_init()
784 v4l2_ctrl_handler_free(&csi->ctrl_handler); in sun6i_csi_v4l2_init()
786 v4l2_async_notifier_cleanup(&csi->notifier); in sun6i_csi_v4l2_init()
787 media_device_cleanup(&csi->media_dev); in sun6i_csi_v4l2_init()
792 /* -----------------------------------------------------------------------------
798 struct regmap *regmap = sdev->regmap; in sun6i_csi_isr()
818 sun6i_video_frame_done(&sdev->csi.video); in sun6i_csi_isr()
841 io_base = devm_ioremap_resource(&pdev->dev, res); in sun6i_csi_resource_request()
845 sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base, in sun6i_csi_resource_request()
847 if (IS_ERR(sdev->regmap)) { in sun6i_csi_resource_request()
848 dev_err(&pdev->dev, "Failed to init register map\n"); in sun6i_csi_resource_request()
849 return PTR_ERR(sdev->regmap); in sun6i_csi_resource_request()
852 sdev->clk_mod = devm_clk_get(&pdev->dev, "mod"); in sun6i_csi_resource_request()
853 if (IS_ERR(sdev->clk_mod)) { in sun6i_csi_resource_request()
854 dev_err(&pdev->dev, "Unable to acquire csi clock\n"); in sun6i_csi_resource_request()
855 return PTR_ERR(sdev->clk_mod); in sun6i_csi_resource_request()
858 sdev->clk_ram = devm_clk_get(&pdev->dev, "ram"); in sun6i_csi_resource_request()
859 if (IS_ERR(sdev->clk_ram)) { in sun6i_csi_resource_request()
860 dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n"); in sun6i_csi_resource_request()
861 return PTR_ERR(sdev->clk_ram); in sun6i_csi_resource_request()
864 sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL); in sun6i_csi_resource_request()
865 if (IS_ERR(sdev->rstc_bus)) { in sun6i_csi_resource_request()
866 dev_err(&pdev->dev, "Cannot get reset controller\n"); in sun6i_csi_resource_request()
867 return PTR_ERR(sdev->rstc_bus); in sun6i_csi_resource_request()
872 return -ENXIO; in sun6i_csi_resource_request()
874 ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME, in sun6i_csi_resource_request()
877 dev_err(&pdev->dev, "Cannot request csi IRQ\n"); in sun6i_csi_resource_request()
897 sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); in sun6i_csi_probe()
899 return -ENOMEM; in sun6i_csi_probe()
901 sdev->dev = &pdev->dev; in sun6i_csi_probe()
908 ret = dma_direct_set_offset(sdev->dev, PHYS_OFFSET, 0, SZ_4G); in sun6i_csi_probe()
918 sdev->csi.dev = &pdev->dev; in sun6i_csi_probe()
919 return sun6i_csi_v4l2_init(&sdev->csi); in sun6i_csi_probe()
926 sun6i_csi_v4l2_cleanup(&sdev->csi); in sun6i_csi_remove()
932 { .compatible = "allwinner,sun6i-a31-csi", },
933 { .compatible = "allwinner,sun8i-a83t-csi", },
934 { .compatible = "allwinner,sun8i-h3-csi", },
935 { .compatible = "allwinner,sun8i-v3s-csi", },
936 { .compatible = "allwinner,sun50i-a64-csi", },