Lines Matching +full:sd +full:- +full:hs
1 // SPDX-License-Identifier: GPL-2.0-only
3 * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
5 * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
29 #include <media/drv-intf/exynos-fimc.h>
30 #include <media/v4l2-fwnode.h>
31 #include <media/v4l2-subdev.h>
33 #include "mipi-csis.h"
37 MODULE_PARM_DESC(debug, "Debug level (0-2)");
51 /* D-PHY control */
62 #define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2)
109 /* Non-image packet data buffers */
153 /* Non-image data receive events */
154 { S5PCSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
155 { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
156 { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
157 { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
175 * struct csis_state - the driver's internal state data structure
179 * @sd: v4l2_subdev associated with CSIS device instance
186 * @irq: requested s5p-mipi-csis irq number
190 * @hs_settle: HS-RX settle time
191 * @num_lanes: number of MIPI-CSI data lanes used
192 * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
193 * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
197 * @pkt_buf: the frame embedded (non-image) data buffer
198 * @events: MIPI-CSIS event (error) counters
203 struct v4l2_subdev sd; member
229 * struct csis_pix_format - CSIS pixel format description
234 * @data_alignment: MIPI-CSI data alignment in bits
271 #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
272 #define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
276 return container_of(sdev, struct csis_state, sd); in sd_to_csis_state()
285 if (mf->code == s5pcsis_formats[i].code) in find_csis_format()
294 val |= state->interrupt_mask; in s5pcsis_enable_interrupts()
296 val &= ~state->interrupt_mask; in s5pcsis_enable_interrupts()
322 mask = (1 << (state->num_lanes + 1)) - 1; in s5pcsis_system_enable()
331 struct v4l2_mbus_framefmt *mf = &state->format; in __s5pcsis_set_format()
334 v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n", in __s5pcsis_set_format()
335 mf->code, mf->width, mf->height); in __s5pcsis_set_format()
339 val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg; in __s5pcsis_set_format()
343 val = (mf->width << 16) | mf->height; in __s5pcsis_set_format()
360 val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); in s5pcsis_set_params()
364 s5pcsis_set_hsync_settle(state, state->hs_settle); in s5pcsis_set_params()
367 if (state->csis_fmt->data_alignment == 32) in s5pcsis_set_params()
369 else /* 24-bits */ in s5pcsis_set_params()
373 if (state->wclk_ext) in s5pcsis_set_params()
387 if (IS_ERR(state->clock[i])) in s5pcsis_clk_put()
389 clk_unprepare(state->clock[i]); in s5pcsis_clk_put()
390 clk_put(state->clock[i]); in s5pcsis_clk_put()
391 state->clock[i] = ERR_PTR(-EINVAL); in s5pcsis_clk_put()
397 struct device *dev = &state->pdev->dev; in s5pcsis_clk_get()
401 state->clock[i] = ERR_PTR(-EINVAL); in s5pcsis_clk_get()
404 state->clock[i] = clk_get(dev, csi_clock_name[i]); in s5pcsis_clk_get()
405 if (IS_ERR(state->clock[i])) { in s5pcsis_clk_get()
406 ret = PTR_ERR(state->clock[i]); in s5pcsis_clk_get()
409 ret = clk_prepare(state->clock[i]); in s5pcsis_clk_get()
411 clk_put(state->clock[i]); in s5pcsis_clk_get()
412 state->clock[i] = ERR_PTR(-EINVAL); in s5pcsis_clk_get()
439 v4l2_info(&state->sd, "--- %s ---\n", label); in dump_regs()
443 v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); in dump_regs()
466 spin_lock_irqsave(&state->slock, flags); in s5pcsis_clear_counters()
468 state->events[i].counter = 0; in s5pcsis_clear_counters()
469 spin_unlock_irqrestore(&state->slock, flags); in s5pcsis_clear_counters()
474 int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4; in s5pcsis_log_counters()
477 spin_lock_irqsave(&state->slock, flags); in s5pcsis_log_counters()
479 for (i--; i >= 0; i--) { in s5pcsis_log_counters()
480 if (state->events[i].counter > 0 || debug) in s5pcsis_log_counters()
481 v4l2_info(&state->sd, "%s events: %d\n", in s5pcsis_log_counters()
482 state->events[i].name, in s5pcsis_log_counters()
483 state->events[i].counter); in s5pcsis_log_counters()
485 spin_unlock_irqrestore(&state->slock, flags); in s5pcsis_log_counters()
491 static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) in s5pcsis_s_power() argument
493 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_s_power()
494 struct device *dev = &state->pdev->dev; in s5pcsis_s_power()
502 static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) in s5pcsis_s_stream() argument
504 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_s_stream()
507 v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n", in s5pcsis_s_stream()
508 __func__, enable, state->flags); in s5pcsis_s_stream()
512 ret = pm_runtime_resume_and_get(&state->pdev->dev); in s5pcsis_s_stream()
517 mutex_lock(&state->lock); in s5pcsis_s_stream()
519 if (state->flags & ST_SUSPENDED) { in s5pcsis_s_stream()
520 ret = -EBUSY; in s5pcsis_s_stream()
524 state->flags |= ST_STREAMING; in s5pcsis_s_stream()
527 state->flags &= ~ST_STREAMING; in s5pcsis_s_stream()
532 mutex_unlock(&state->lock); in s5pcsis_s_stream()
534 pm_runtime_put(&state->pdev->dev); in s5pcsis_s_stream()
539 static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, in s5pcsis_enum_mbus_code() argument
543 if (code->index >= ARRAY_SIZE(s5pcsis_formats)) in s5pcsis_enum_mbus_code()
544 return -EINVAL; in s5pcsis_enum_mbus_code()
546 code->code = s5pcsis_formats[code->index].code; in s5pcsis_enum_mbus_code()
559 mf->code = csis_fmt->code; in s5pcsis_try_format()
560 v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, in s5pcsis_try_format()
561 csis_fmt->pix_width_alignment, in s5pcsis_try_format()
562 &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, in s5pcsis_try_format()
574 return &state->format; in __s5pcsis_get_format()
577 static int s5pcsis_set_fmt(struct v4l2_subdev *sd, in s5pcsis_set_fmt() argument
581 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_set_fmt()
585 mf = __s5pcsis_get_format(state, sd_state, fmt->which); in s5pcsis_set_fmt()
587 if (fmt->pad == CSIS_PAD_SOURCE) { in s5pcsis_set_fmt()
589 mutex_lock(&state->lock); in s5pcsis_set_fmt()
590 fmt->format = *mf; in s5pcsis_set_fmt()
591 mutex_unlock(&state->lock); in s5pcsis_set_fmt()
595 csis_fmt = s5pcsis_try_format(&fmt->format); in s5pcsis_set_fmt()
597 mutex_lock(&state->lock); in s5pcsis_set_fmt()
598 *mf = fmt->format; in s5pcsis_set_fmt()
599 if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) in s5pcsis_set_fmt()
600 state->csis_fmt = csis_fmt; in s5pcsis_set_fmt()
601 mutex_unlock(&state->lock); in s5pcsis_set_fmt()
606 static int s5pcsis_get_fmt(struct v4l2_subdev *sd, in s5pcsis_get_fmt() argument
610 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_get_fmt()
613 mf = __s5pcsis_get_format(state, sd_state, fmt->which); in s5pcsis_get_fmt()
615 return -EINVAL; in s5pcsis_get_fmt()
617 mutex_lock(&state->lock); in s5pcsis_get_fmt()
618 fmt->format = *mf; in s5pcsis_get_fmt()
619 mutex_unlock(&state->lock); in s5pcsis_get_fmt()
623 static int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf, in s5pcsis_s_rx_buffer() argument
626 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_s_rx_buffer()
631 spin_lock_irqsave(&state->slock, flags); in s5pcsis_s_rx_buffer()
632 state->pkt_buf.data = buf; in s5pcsis_s_rx_buffer()
633 state->pkt_buf.len = *size; in s5pcsis_s_rx_buffer()
634 spin_unlock_irqrestore(&state->slock, flags); in s5pcsis_s_rx_buffer()
639 static int s5pcsis_log_status(struct v4l2_subdev *sd) in s5pcsis_log_status() argument
641 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_log_status()
643 mutex_lock(&state->lock); in s5pcsis_log_status()
645 if (debug && (state->flags & ST_POWERED)) in s5pcsis_log_status()
647 mutex_unlock(&state->lock); in s5pcsis_log_status()
676 struct csis_pktbuf *pktbuf = &state->pkt_buf; in s5pcsis_irq_handler()
681 spin_lock_irqsave(&state->slock, flags); in s5pcsis_irq_handler()
683 if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { in s5pcsis_irq_handler()
691 memcpy(pktbuf->data, (u8 __force *)state->regs + offset, in s5pcsis_irq_handler()
692 pktbuf->len); in s5pcsis_irq_handler()
693 pktbuf->data = NULL; in s5pcsis_irq_handler()
701 if (!(status & state->events[i].mask)) in s5pcsis_irq_handler()
703 state->events[i].counter++; in s5pcsis_irq_handler()
704 v4l2_dbg(2, debug, &state->sd, "%s: %d\n", in s5pcsis_irq_handler()
705 state->events[i].name, in s5pcsis_irq_handler()
706 state->events[i].counter); in s5pcsis_irq_handler()
708 v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status); in s5pcsis_irq_handler()
710 spin_unlock_irqrestore(&state->slock, flags); in s5pcsis_irq_handler()
719 struct device_node *node = pdev->dev.of_node; in s5pcsis_parse_dt()
723 if (of_property_read_u32(node, "clock-frequency", in s5pcsis_parse_dt()
724 &state->clk_frequency)) in s5pcsis_parse_dt()
725 state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; in s5pcsis_parse_dt()
726 if (of_property_read_u32(node, "bus-width", in s5pcsis_parse_dt()
727 &state->max_num_lanes)) in s5pcsis_parse_dt()
728 return -EINVAL; in s5pcsis_parse_dt()
732 dev_err(&pdev->dev, "No port node at %pOF\n", in s5pcsis_parse_dt()
733 pdev->dev.of_node); in s5pcsis_parse_dt()
734 return -EINVAL; in s5pcsis_parse_dt()
736 /* Get port node and validate MIPI-CSI channel id. */ in s5pcsis_parse_dt()
741 state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; in s5pcsis_parse_dt()
742 if (state->index >= CSIS_MAX_ENTITIES) { in s5pcsis_parse_dt()
743 ret = -ENXIO; in s5pcsis_parse_dt()
747 /* Get MIPI CSI-2 bus configuration from the endpoint node. */ in s5pcsis_parse_dt()
748 of_property_read_u32(node, "samsung,csis-hs-settle", in s5pcsis_parse_dt()
749 &state->hs_settle); in s5pcsis_parse_dt()
750 state->wclk_ext = of_property_read_bool(node, in s5pcsis_parse_dt()
751 "samsung,csis-wclk"); in s5pcsis_parse_dt()
753 state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; in s5pcsis_parse_dt()
767 struct device *dev = &pdev->dev; in s5pcsis_probe()
769 int ret = -ENOMEM; in s5pcsis_probe()
774 return -ENOMEM; in s5pcsis_probe()
776 mutex_init(&state->lock); in s5pcsis_probe()
777 spin_lock_init(&state->slock); in s5pcsis_probe()
778 state->pdev = pdev; in s5pcsis_probe()
780 of_id = of_match_node(s5pcsis_of_match, dev->of_node); in s5pcsis_probe()
782 return -EINVAL; in s5pcsis_probe()
784 drv_data = of_id->data; in s5pcsis_probe()
785 state->interrupt_mask = drv_data->interrupt_mask; in s5pcsis_probe()
791 if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { in s5pcsis_probe()
793 state->num_lanes, state->max_num_lanes); in s5pcsis_probe()
794 return -EINVAL; in s5pcsis_probe()
797 state->phy = devm_phy_get(dev, "csis"); in s5pcsis_probe()
798 if (IS_ERR(state->phy)) in s5pcsis_probe()
799 return PTR_ERR(state->phy); in s5pcsis_probe()
801 state->regs = devm_platform_ioremap_resource(pdev, 0); in s5pcsis_probe()
802 if (IS_ERR(state->regs)) in s5pcsis_probe()
803 return PTR_ERR(state->regs); in s5pcsis_probe()
805 state->irq = platform_get_irq(pdev, 0); in s5pcsis_probe()
806 if (state->irq < 0) in s5pcsis_probe()
807 return state->irq; in s5pcsis_probe()
810 state->supplies[i].supply = csis_supply_name[i]; in s5pcsis_probe()
813 state->supplies); in s5pcsis_probe()
821 if (state->clk_frequency) in s5pcsis_probe()
822 ret = clk_set_rate(state->clock[CSIS_CLK_MUX], in s5pcsis_probe()
823 state->clk_frequency); in s5pcsis_probe()
829 ret = clk_enable(state->clock[CSIS_CLK_MUX]); in s5pcsis_probe()
833 ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, in s5pcsis_probe()
840 v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); in s5pcsis_probe()
841 state->sd.owner = THIS_MODULE; in s5pcsis_probe()
842 snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", in s5pcsis_probe()
843 CSIS_SUBDEV_NAME, state->index); in s5pcsis_probe()
844 state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; in s5pcsis_probe()
845 state->csis_fmt = &s5pcsis_formats[0]; in s5pcsis_probe()
847 state->format.code = s5pcsis_formats[0].code; in s5pcsis_probe()
848 state->format.width = S5PCSIS_DEF_PIX_WIDTH; in s5pcsis_probe()
849 state->format.height = S5PCSIS_DEF_PIX_HEIGHT; in s5pcsis_probe()
851 state->sd.entity.function = MEDIA_ENT_F_IO_V4L; in s5pcsis_probe()
852 state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; in s5pcsis_probe()
853 state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; in s5pcsis_probe()
854 ret = media_entity_pads_init(&state->sd.entity, in s5pcsis_probe()
855 CSIS_PADS_NUM, state->pads); in s5pcsis_probe()
860 v4l2_set_subdevdata(&state->sd, pdev); in s5pcsis_probe()
863 platform_set_drvdata(pdev, &state->sd); in s5pcsis_probe()
864 memcpy(state->events, s5pcsis_events, sizeof(state->events)); in s5pcsis_probe()
873 dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", in s5pcsis_probe()
874 state->num_lanes, state->hs_settle, state->wclk_ext, in s5pcsis_probe()
875 state->clk_frequency); in s5pcsis_probe()
879 media_entity_cleanup(&state->sd.entity); in s5pcsis_probe()
881 clk_disable(state->clock[CSIS_CLK_MUX]); in s5pcsis_probe()
889 struct v4l2_subdev *sd = dev_get_drvdata(dev); in s5pcsis_pm_suspend() local
890 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_pm_suspend()
893 v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", in s5pcsis_pm_suspend()
894 __func__, state->flags); in s5pcsis_pm_suspend()
896 mutex_lock(&state->lock); in s5pcsis_pm_suspend()
897 if (state->flags & ST_POWERED) { in s5pcsis_pm_suspend()
899 ret = phy_power_off(state->phy); in s5pcsis_pm_suspend()
903 state->supplies); in s5pcsis_pm_suspend()
906 clk_disable(state->clock[CSIS_CLK_GATE]); in s5pcsis_pm_suspend()
907 state->flags &= ~ST_POWERED; in s5pcsis_pm_suspend()
909 state->flags |= ST_SUSPENDED; in s5pcsis_pm_suspend()
912 mutex_unlock(&state->lock); in s5pcsis_pm_suspend()
913 return ret ? -EAGAIN : 0; in s5pcsis_pm_suspend()
918 struct v4l2_subdev *sd = dev_get_drvdata(dev); in s5pcsis_pm_resume() local
919 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_pm_resume()
922 v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", in s5pcsis_pm_resume()
923 __func__, state->flags); in s5pcsis_pm_resume()
925 mutex_lock(&state->lock); in s5pcsis_pm_resume()
926 if (!runtime && !(state->flags & ST_SUSPENDED)) in s5pcsis_pm_resume()
929 if (!(state->flags & ST_POWERED)) { in s5pcsis_pm_resume()
931 state->supplies); in s5pcsis_pm_resume()
934 ret = phy_power_on(state->phy); in s5pcsis_pm_resume()
936 state->flags |= ST_POWERED; in s5pcsis_pm_resume()
939 state->supplies); in s5pcsis_pm_resume()
942 clk_enable(state->clock[CSIS_CLK_GATE]); in s5pcsis_pm_resume()
944 if (state->flags & ST_STREAMING) in s5pcsis_pm_resume()
947 state->flags &= ~ST_SUSPENDED; in s5pcsis_pm_resume()
949 mutex_unlock(&state->lock); in s5pcsis_pm_resume()
950 return ret ? -EAGAIN : 0; in s5pcsis_pm_resume()
979 struct v4l2_subdev *sd = platform_get_drvdata(pdev); in s5pcsis_remove() local
980 struct csis_state *state = sd_to_csis_state(sd); in s5pcsis_remove()
982 pm_runtime_disable(&pdev->dev); in s5pcsis_remove()
983 s5pcsis_pm_suspend(&pdev->dev, true); in s5pcsis_remove()
984 clk_disable(state->clock[CSIS_CLK_MUX]); in s5pcsis_remove()
985 pm_runtime_set_suspended(&pdev->dev); in s5pcsis_remove()
988 media_entity_cleanup(&state->sd.entity); in s5pcsis_remove()
1007 .compatible = "samsung,s5pv210-csis",
1010 .compatible = "samsung,exynos4210-csis",
1013 .compatible = "samsung,exynos5250-csis",
1033 MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");