Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bbde3104 authored by Todor Tomov's avatar Todor Tomov Committed by Mauro Carvalho Chehab
Browse files

media: camss: Use optimal clock frequency rates



Use standard V4L2 control to get pixel clock rate from a sensor
linked in the media controller pipeline. Then calculate clock
rates on CSIPHY, CSID and VFE to use the lowest possible.

If the currnet pixel clock rate of the sensor cannot be read then
use the highest possible. This case covers also the CSID test
generator usage.

If VFE is already powered on by another pipeline, check that the
current VFE clock rate is high enough for the new pipeline.
If not return busy error code as VFE clock rate cannot be changed
while VFE is running.

Signed-off-by: default avatarTodor Tomov <todor.tomov@linaro.org>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 474a53d6
Loading
Loading
Loading
Loading
+114 −25
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ struct csid_fmts {
	u8 data_type;
	u8 decode_format;
	u8 bpp;
	u8 spp; /* bus samples per pixel */
};

static const struct csid_fmts csid_input_fmts[] = {
@@ -76,97 +77,113 @@ static const struct csid_fmts csid_input_fmts[] = {
		MEDIA_BUS_FMT_UYVY8_2X8,
		DATA_TYPE_YUV422_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		16,
		8,
		2,
	},
	{
		MEDIA_BUS_FMT_VYUY8_2X8,
		DATA_TYPE_YUV422_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		16,
		8,
		2,
	},
	{
		MEDIA_BUS_FMT_YUYV8_2X8,
		DATA_TYPE_YUV422_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		16,
		8,
		2,
	},
	{
		MEDIA_BUS_FMT_YVYU8_2X8,
		DATA_TYPE_YUV422_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		16,
		8,
		2,
	},
	{
		MEDIA_BUS_FMT_SBGGR8_1X8,
		DATA_TYPE_RAW_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		8,
		1,
	},
	{
		MEDIA_BUS_FMT_SGBRG8_1X8,
		DATA_TYPE_RAW_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		8,
		1,
	},
	{
		MEDIA_BUS_FMT_SGRBG8_1X8,
		DATA_TYPE_RAW_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		8,
		1,
	},
	{
		MEDIA_BUS_FMT_SRGGB8_1X8,
		DATA_TYPE_RAW_8BIT,
		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
		8,
		1,
	},
	{
		MEDIA_BUS_FMT_SBGGR10_1X10,
		DATA_TYPE_RAW_10BIT,
		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
		10,
		1,
	},
	{
		MEDIA_BUS_FMT_SGBRG10_1X10,
		DATA_TYPE_RAW_10BIT,
		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
		10,
		1,
	},
	{
		MEDIA_BUS_FMT_SGRBG10_1X10,
		DATA_TYPE_RAW_10BIT,
		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
		10,
		1,
	},
	{
		MEDIA_BUS_FMT_SRGGB10_1X10,
		DATA_TYPE_RAW_10BIT,
		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
		10,
		1,
	},
	{
		MEDIA_BUS_FMT_SBGGR12_1X12,
		DATA_TYPE_RAW_12BIT,
		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
		12,
		1,
	},
	{
		MEDIA_BUS_FMT_SGBRG12_1X12,
		DATA_TYPE_RAW_12BIT,
		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
		12,
		1,
	},
	{
		MEDIA_BUS_FMT_SGRBG12_1X12,
		DATA_TYPE_RAW_12BIT,
		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
		12,
		1,
	},
	{
		MEDIA_BUS_FMT_SRGGB12_1X12,
		DATA_TYPE_RAW_12BIT,
		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
		12,
		1,
	}
};

@@ -204,6 +221,67 @@ static irqreturn_t csid_isr(int irq, void *dev)
	return IRQ_HANDLED;
}

/*
 * csid_set_clock_rates - Calculate and set clock rates on CSID module
 * @csiphy: CSID device
 */
static int csid_set_clock_rates(struct csid_device *csid)
{
	struct device *dev = to_device_index(csid, csid->id);
	u32 pixel_clock;
	int i, j;
	int ret;

	ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock);
	if (ret)
		pixel_clock = 0;

	for (i = 0; i < csid->nclocks; i++) {
		struct camss_clock *clock = &csid->clock[i];

		if (!strcmp(clock->name, "csi0") ||
			!strcmp(clock->name, "csi1")) {
			u8 bpp = csid_get_fmt_entry(
				csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp;
			u8 num_lanes = csid->phy.lane_cnt;
			u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
			long rate;

			camss_add_clock_margin(&min_rate);

			for (j = 0; j < clock->nfreqs; j++)
				if (min_rate < clock->freq[j])
					break;

			if (j == clock->nfreqs) {
				dev_err(dev,
					"Pixel clock is too high for CSID\n");
				return -EINVAL;
			}

			/* if sensor pixel clock is not available */
			/* set highest possible CSID clock rate */
			if (min_rate == 0)
				j = clock->nfreqs - 1;

			rate = clk_round_rate(clock->clk, clock->freq[j]);
			if (rate < 0) {
				dev_err(dev, "clk round rate failed: %ld\n",
					rate);
				return -EINVAL;
			}

			ret = clk_set_rate(clock->clk, rate);
			if (ret < 0) {
				dev_err(dev, "clk set rate failed: %d\n", ret);
				return ret;
			}
		}
	}

	return 0;
}

/*
 * csid_reset - Trigger reset on CSID module and wait to complete
 * @csid: CSID device
@@ -249,6 +327,12 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
		if (ret < 0)
			return ret;

		ret = csid_set_clock_rates(csid);
		if (ret < 0) {
			regulator_disable(csid->vdda);
			return ret;
		}

		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
		if (ret < 0) {
			regulator_disable(csid->vdda);
@@ -316,7 +400,8 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
			struct v4l2_mbus_framefmt *f =
					&csid->fmt[MSM_CSID_PAD_SRC];
			u8 bpp = csid_get_fmt_entry(f->code)->bpp;
			u32 num_bytes_per_line = f->width * bpp / 8;
			u8 spp = csid_get_fmt_entry(f->code)->spp;
			u32 num_bytes_per_line = f->width * bpp * spp / 8;
			u32 num_lines = f->height;

			/* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
@@ -719,7 +804,7 @@ int msm_csid_subdev_init(struct csid_device *csid,
	struct device *dev = to_device_index(csid, id);
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *r;
	int i;
	int i, j;
	int ret;

	csid->id = id;
@@ -766,26 +851,30 @@ int msm_csid_subdev_init(struct csid_device *csid,
		return -ENOMEM;

	for (i = 0; i < csid->nclocks; i++) {
		csid->clock[i] = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(csid->clock[i]))
			return PTR_ERR(csid->clock[i]);

		if (res->clock_rate[i]) {
			long clk_rate = clk_round_rate(csid->clock[i],
						       res->clock_rate[i]);
			if (clk_rate < 0) {
				dev_err(to_device_index(csid, csid->id),
					"clk round rate failed: %ld\n",
					clk_rate);
				return -EINVAL;
			}
			ret = clk_set_rate(csid->clock[i], clk_rate);
			if (ret < 0) {
				dev_err(to_device_index(csid, csid->id),
					"clk set rate failed: %d\n", ret);
				return ret;
			}
		struct camss_clock *clock = &csid->clock[i];

		clock->clk = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(clock->clk))
			return PTR_ERR(clock->clk);

		clock->name = res->clock[i];

		clock->nfreqs = 0;
		while (res->clock_rate[i][clock->nfreqs])
			clock->nfreqs++;

		if (!clock->nfreqs) {
			clock->freq = NULL;
			continue;
		}

		clock->freq = devm_kzalloc(dev, clock->nfreqs *
					   sizeof(*clock->freq), GFP_KERNEL);
		if (!clock->freq)
			return -ENOMEM;

		for (j = 0; j < clock->nfreqs; j++)
			clock->freq[j] = res->clock_rate[i][j];
	}

	/* Regulator */
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ struct csid_device {
	void __iomem *base;
	u32 irq;
	char irq_name[30];
	struct clk **clock;
	struct camss_clock *clock;
	int nclocks;
	struct regulator *vdda;
	struct completion reset_complete;
+90 −23
Original line number Diff line number Diff line
@@ -157,6 +157,69 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
	return IRQ_HANDLED;
}

/*
 * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module
 * @csiphy: CSIPHY device
 */
static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
{
	struct device *dev = to_device_index(csiphy, csiphy->id);
	u32 pixel_clock;
	int i, j;
	int ret;

	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
	if (ret)
		pixel_clock = 0;

	for (i = 0; i < csiphy->nclocks; i++) {
		struct camss_clock *clock = &csiphy->clock[i];

		if (!strcmp(clock->name, "csiphy0_timer") ||
			!strcmp(clock->name, "csiphy1_timer")) {
			u8 bpp = csiphy_get_bpp(
					csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
			u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
			u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
			long round_rate;

			camss_add_clock_margin(&min_rate);

			for (j = 0; j < clock->nfreqs; j++)
				if (min_rate < clock->freq[j])
					break;

			if (j == clock->nfreqs) {
				dev_err(dev,
					"Pixel clock is too high for CSIPHY\n");
				return -EINVAL;
			}

			/* if sensor pixel clock is not available */
			/* set highest possible CSIPHY clock rate */
			if (min_rate == 0)
				j = clock->nfreqs - 1;

			round_rate = clk_round_rate(clock->clk, clock->freq[j]);
			if (round_rate < 0) {
				dev_err(dev, "clk round rate failed: %ld\n",
					round_rate);
				return -EINVAL;
			}

			csiphy->timer_clk_rate = round_rate;

			ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate);
			if (ret < 0) {
				dev_err(dev, "clk set rate failed: %d\n", ret);
				return ret;
			}
		}
	}

	return 0;
}

/*
 * csiphy_reset - Perform software reset on CSIPHY module
 * @csiphy: CSIPHY device
@@ -184,6 +247,10 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
		u8 hw_version;
		int ret;

		ret = csiphy_set_clock_rates(csiphy);
		if (ret < 0)
			return ret;

		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
		if (ret < 0)
			return ret;
@@ -616,7 +683,7 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
	struct device *dev = to_device_index(csiphy, id);
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *r;
	int i;
	int i, j;
	int ret;

	csiphy->id = id;
@@ -671,30 +738,30 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
		return -ENOMEM;

	for (i = 0; i < csiphy->nclocks; i++) {
		csiphy->clock[i] = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(csiphy->clock[i]))
			return PTR_ERR(csiphy->clock[i]);

		if (res->clock_rate[i]) {
			long clk_rate = clk_round_rate(csiphy->clock[i],
						       res->clock_rate[i]);
			if (clk_rate < 0) {
				dev_err(to_device_index(csiphy, csiphy->id),
					"clk round rate failed: %ld\n",
					clk_rate);
				return -EINVAL;
			}
			ret = clk_set_rate(csiphy->clock[i], clk_rate);
			if (ret < 0) {
				dev_err(to_device_index(csiphy, csiphy->id),
					"clk set rate failed: %d\n", ret);
				return ret;
			}
		struct camss_clock *clock = &csiphy->clock[i];

			if (!strcmp(res->clock[i], "csiphy0_timer") ||
					!strcmp(res->clock[i], "csiphy1_timer"))
				csiphy->timer_clk_rate = clk_rate;
		clock->clk = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(clock->clk))
			return PTR_ERR(clock->clk);

		clock->name = res->clock[i];

		clock->nfreqs = 0;
		while (res->clock_rate[i][clock->nfreqs])
			clock->nfreqs++;

		if (!clock->nfreqs) {
			clock->freq = NULL;
			continue;
		}

		clock->freq = devm_kzalloc(dev, clock->nfreqs *
					   sizeof(*clock->freq), GFP_KERNEL);
		if (!clock->freq)
			return -ENOMEM;

		for (j = 0; j < clock->nfreqs; j++)
			clock->freq[j] = res->clock_rate[i][j];
	}

	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ struct csiphy_device {
	void __iomem *base_clk_mux;
	u32 irq;
	char irq_name[30];
	struct clk **clock;
	struct camss_clock *clock;
	int nclocks;
	u32 timer_clk_rate;
	struct csiphy_config cfg;
+16 −7
Original line number Diff line number Diff line
@@ -954,9 +954,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
		return -ENOMEM;

	for (i = 0; i < ispif->nclocks; i++) {
		ispif->clock[i] = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(ispif->clock[i]))
			return PTR_ERR(ispif->clock[i]);
		struct camss_clock *clock = &ispif->clock[i];

		clock->clk = devm_clk_get(dev, res->clock[i]);
		if (IS_ERR(clock->clk))
			return PTR_ERR(clock->clk);

		clock->freq = NULL;
		clock->nfreqs = 0;
	}

	ispif->nclocks_for_reset = 0;
@@ -969,10 +974,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
		return -ENOMEM;

	for (i = 0; i < ispif->nclocks_for_reset; i++) {
		ispif->clock_for_reset[i] = devm_clk_get(dev,
						res->clock_for_reset[i]);
		if (IS_ERR(ispif->clock_for_reset[i]))
			return PTR_ERR(ispif->clock_for_reset[i]);
		struct camss_clock *clock = &ispif->clock_for_reset[i];

		clock->clk = devm_clk_get(dev, res->clock_for_reset[i]);
		if (IS_ERR(clock->clk))
			return PTR_ERR(clock->clk);

		clock->freq = NULL;
		clock->nfreqs = 0;
	}

	for (i = 0; i < ARRAY_SIZE(ispif->line); i++)
Loading