Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unicam clock changes #3609

Merged
merged 2 commits into from
Jul 13, 2020
Merged

unicam clock changes #3609

merged 2 commits into from
Jul 13, 2020

Conversation

naushir
Copy link
Contributor

@naushir naushir commented May 14, 2020

Add control of the vpu clock when streaming from unicam.

@naushir
Copy link
Contributor Author

naushir commented May 14, 2020

@pelwell and @6by9 comments please.

@pelwell
Copy link
Contributor

pelwell commented May 14, 2020

I'm not wild about the magic number 250 * 1000 * 1000 in the code as opposed to a macro constant, but it is only used once. Is it possible that this value might ever change? Should it be a DT parameter?

I was going to raise some concerns about the SET_CLOCK_RATE mechanism, but I see that the firmware does the right thing and uses the "boost" mechanism, meaning that you won't reduce the clock if something else wants it to be higher. There appears to be no way to relinquish a claim on the clock minimum frequency, but is 0 a better minimum than 200MHz?

My only remaining concern is over whether firmware-clocks needs a similar "boost" accounting mechanism as the firmware - what happens when one stream stops while the other is still going? What stops the core frequency from being reduced?

@naushir
Copy link
Contributor Author

naushir commented May 14, 2020

I'm not wild about the magic number 250 * 1000 * 1000 in the code as opposed to a macro constant, but it is only used once. Is it possible that this value might ever change? Should it be a DT parameter?

Like the LP clock, this will (likely) never change. Having it as a DT parameter is reasonable, but perhaps overkill?

I was going to raise some concerns about the SET_CLOCK_RATE mechanism, but I see that the firmware does the right thing and uses the "boost" mechanism, meaning that you won't reduce the clock if something else wants it to be higher. There appears to be no way to relinquish a claim on the clock minimum frequency, but is 0 a better minimum than 200MHz?

Dave and me discussed this very point, and we ended up setting it back to 200MHz like the HVS does.

My only remaining concern is over whether firmware-clocks needs a similar "boost" accounting mechanism as the firmware - what happens when one stream stops while the other is still going? What stops the core frequency from being reduced?

Yes, this is a good point. I had actually assumed that firmware-clock already did this, will need to go look at the code in more detail.

@popcornmix
Copy link
Collaborator

Max/min rates can be read from firmware, but I don't think the clock driver exposes that conveniently:
https://github.com/raspberrypi/linux/blob/rpi-5.4.y/drivers/clk/bcm/clk-raspberrypi.c#L215-L232

Overlapping calls to control the same clock are not safe. Returning a cookie when boosting that is passed back in when unboosted is how the firmware deals with this but I don't know how that can be done though the clock interface.

I don't know if something unique to the caller could be passed into the (extended) RPI_FIRMWARE_SET_CLOCK_RATE call. Similar to the pid passed into vchiq messages. The firmware could then keep track of multiple requests.

@6by9
Copy link
Contributor

6by9 commented May 14, 2020

I did look at the max/min clock queries that can be done, but currently it's only there for PLLB (ARM clock). All the other clocks just get added.
It looks like it's used in determine_rate for PLLB to clip the range, but I don't see a way of querying the it externally.

@mripard may be able to advise if there is a range check within the clock APIs, and confirm whether the clock framework deals with refcounting clock requests (I seem to recall him saying it does).

@mripard
Copy link
Contributor

mripard commented May 19, 2020

I did look at the max/min clock queries that can be done, but currently it's only there for PLLB (ARM clock). All the other clocks just get added.

I didn't add it since I wasn't aware of any use case for it before, but it's pretty trivial to add it if we need it.

It looks like it's used in determine_rate for PLLB to clip the range, but I don't see a way of querying the it externally.

Indeed it's only used for clipping at the moment and you can't retrieve it from the user API. It could be added I think if we have a use case for it. I'm not sure what the issue addressed in your patches is though. You need to have the VPU clock running at least at 250MHz while the unicam is running, and 200MHz otherwise?

@mripard may be able to advise if there is a range check within the clock APIs, and confirm whether the clock framework deals with refcounting clock requests (I seem to recall him saying it does).

The clock framework has some refcounting (on enable / disable for example), but not on all operations (set_rate doesn't for example). What kind of requests are we talking about here?

@pelwell
Copy link
Contributor

pelwell commented May 19, 2020

The aim is address my question above:

My only remaining concern is over whether firmware-clocks needs a similar "boost" accounting mechanism as the firmware - what happens when one stream stops while the other is still going? What stops the core frequency from being reduced?

The clock driver is a single client of the firmware but represents multiple clients within the kernel. Since the aim is to guarantee a minimum clock speed then something in either unicam or the clock driver needs to track how many streams are active and request the higher clock when the count is non-zero.

My suggestion would be to add aliases of (at least) the core clock and get each of the unicam instances to use different aliases, allowing multiple uses to be tracked independently, unless you feel like making a more sweeping change.

@popcornmix
Copy link
Collaborator

vc4 driver also boosts core clock during a mode set. I think that's the only other "official" user. User apps could also poke the mailbox directly.

@pelwell
Copy link
Contributor

pelwell commented May 19, 2020

User apps could also poke the mailbox directly.

Those users should expect bad stuff to happen occasionally.

@popcornmix
Copy link
Collaborator

Looks like:

min_clock = clk_round_rate(clk, 0);
max_clock = clk_round_rate(clk, ULONG_MAX);

if what's wanted rather than hardd coded numbers.
Request max_clock when busy and min_clock when idle.

@mripard
Copy link
Contributor

mripard commented May 19, 2020

The aim is address my question above:

My only remaining concern is over whether firmware-clocks needs a similar "boost" accounting mechanism as the firmware - what happens when one stream stops while the other is still going? What stops the core frequency from being reduced?

The clock driver is a single client of the firmware but represents multiple clients within the kernel. Since the aim is to guarantee a minimum clock speed then something in either unicam or the clock driver needs to track how many streams are active and request the higher clock when the count is non-zero.

My suggestion would be to add aliases of (at least) the core clock and get each of the unicam instances to use different aliases, allowing multiple uses to be tracked independently, unless you feel like making a more sweeping change.

Does that minimum clock speed change from one user to the other, or is it the same across all users?

If it's the same rate for any user, then we could use the prepare / unprepare callbacks in the clock driver that are called any time a first / last user enables / disables the clock, and use them to raise it to the needed minimum rate, and back to the lower frequency once no-one enables it anymore.

@naushir
Copy link
Contributor Author

naushir commented May 20, 2020

Does that minimum clock speed change from one user to the other, or is it the same across all users?

I expect it could be different per user. Unicam needs 250 MHz, where as HVS currently uses 500 MHz.

@mripard
Copy link
Contributor

mripard commented May 20, 2020

Does that minimum clock speed change from one user to the other, or is it the same across all users?

I expect it could be different per user. Unicam needs 250 MHz, where as HVS currently uses 500 MHz.

In the HVS case, it's only when changing a mode, so it's not really the same thing than raising the clock when it starts to have a user?

@pelwell
Copy link
Contributor

pelwell commented May 20, 2020

I don't think there's a way to get a satisfactory solution without looking at the clock rates requested at runtime from all users and calculating the maximum.

@mripard
Copy link
Contributor

mripard commented May 20, 2020

Then I guess each user should call clk_set_min_rate with its requirement, and the clock framework will figure it out on its own between the hardware min and max (reported by the firmware in our case), and what has been set by each user.

@pelwell
Copy link
Contributor

pelwell commented May 20, 2020

Is there really that extra level between the clock provider and the clock consumer? Do multiple callers to of_clk_get* get back different struct clk *s so the framework can distinguish between them?

@mripard
Copy link
Contributor

mripard commented May 20, 2020

Yes, struct clk is only the customer facing side of the clock representation, and each user will have a different struct clk affected to it. The core uses the structs clk_core and clk_hw (clk_core for the generic parts, clk_hw is what's passed to the drivers) to represent each clocks.

You can assign min and max rates on clk_core (to reflect boundaries of the hardware), or each user can do so on its struct clk (to reflect boundaries on what that particular user needs to be enforced).

@pelwell
Copy link
Contributor

pelwell commented May 20, 2020

OK - found the per-caller struct clk allocation (with shared clk_hw), but clk_set_rate_range (the implementation of clk_set_min_rate) doesn't appear to share the limit changes, i.e. although applying a minimum might immediately change the clock rate, I don't see how it could prevent somebody else from changing it.

Anyway, I'll let you worry about that.

@mripard
Copy link
Contributor

mripard commented May 27, 2020

So I took some time to confirm this this morning, and it's indeed pretty hard to see how it's occuring, but I believe it should work as expected.

When calling clk_set_rate_range, if the current rate of the clock is outside of the range we want to enforce, it will call clk_core_set_rate_nolock to force the rate into the range:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2323

clk_set_rate_nolock will then call clk_core_req_round_rate_nolock on that new rate:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2158

And clk_core_req_round_rate_nolock will call clk_core_get_boundaries
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2137

clk_core_get_boundaries is the function that will retrieve the range for all the users, and the clock provider, and will find the common range
https://elixir.bootlin.com/linux/v5.7-rc7/source/drivers/clk/clk.c#L618

clk_core_req_round_rate_nolock will then call clk_core_round_rate_nolock using the new rate we want to enforce and the boundaries it discovered, but will not clamp it itself.

It might be a bug there, but at least there's an attempt to do what we were discussing :)

@naushir
Copy link
Contributor Author

naushir commented May 28, 2020

So I took some time to confirm this this morning, and it's indeed pretty hard to see how it's occuring, but I believe it should work as expected.

When calling clk_set_rate_range, if the current rate of the clock is outside of the range we want to enforce, it will call clk_core_set_rate_nolock to force the rate into the range:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2323

clk_set_rate_nolock will then call clk_core_req_round_rate_nolock on that new rate:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2158

And clk_core_req_round_rate_nolock will call clk_core_get_boundaries
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2137

clk_core_get_boundaries is the function that will retrieve the range for all the users, and the clock provider, and will find the common range
https://elixir.bootlin.com/linux/v5.7-rc7/source/drivers/clk/clk.c#L618

clk_core_req_round_rate_nolock will then call clk_core_round_rate_nolock using the new rate we want to enforce and the boundaries it discovered, but will not clamp it itself.

It might be a bug there, but at least there's an attempt to do what we were discussing :)

So with all of that, is the correct thing to do as follows:

old_min_rate = clk_round_rate(clk, 0);
clk_set_min_rate(250Mhz);
/* Do stuff in Unicam */
clk_set_min_rate(old_min_rate);

?

@naushir
Copy link
Contributor Author

naushir commented May 29, 2020

So with all of that, is the correct thing to do as follows:

old_min_rate = clk_round_rate(clk, 0);
clk_set_min_rate(250Mhz);
/* Do stuff in Unicam */
clk_set_min_rate(old_min_rate);

?

Sadly this does not work as expected. clk_round_rate(clk, 0); will always return 0. Need to understand what it does exactly.

@popcornmix
Copy link
Collaborator

I believe it works for arm clock in cpufreq driver.
I wonder if it works any better with @mripard 's patches. Specifically patch 12 here:
https://lkml.org/lkml/2020/5/27/957

@mripard
Copy link
Contributor

mripard commented May 29, 2020

So I took some time to confirm this this morning, and it's indeed pretty hard to see how it's occuring, but I believe it should work as expected.
When calling clk_set_rate_range, if the current rate of the clock is outside of the range we want to enforce, it will call clk_core_set_rate_nolock to force the rate into the range:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2323
clk_set_rate_nolock will then call clk_core_req_round_rate_nolock on that new rate:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2158
And clk_core_req_round_rate_nolock will call clk_core_get_boundaries
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L2137
clk_core_get_boundaries is the function that will retrieve the range for all the users, and the clock provider, and will find the common range
https://elixir.bootlin.com/linux/v5.7-rc7/source/drivers/clk/clk.c#L618
clk_core_req_round_rate_nolock will then call clk_core_round_rate_nolock using the new rate we want to enforce and the boundaries it discovered, but will not clamp it itself.
It might be a bug there, but at least there's an attempt to do what we were discussing :)

So with all of that, is the correct thing to do as follows:

old_min_rate = clk_round_rate(clk, 0);
clk_set_min_rate(250Mhz);
/* Do stuff in Unicam */
clk_set_min_rate(old_min_rate);

?

I guess you can even simplify that by just doing something like

clk_set_min_rate(250000000);
...
clk_set_min_rate(0);

But yeah, like @popcornmix was mentionning, this was not properly reported to the CCF at the moment. I can send a PR for the additional fixes I sent to the firmware clocks on that last series if you want?

@popcornmix
Copy link
Collaborator

@mripard a PR for upstream clock fixes would be welcome.

@naushir
Copy link
Contributor Author

naushir commented Jun 9, 2020

@mripard a PR for upstream clock fixes would be welcome.

@mripard, could you ping me when you get this done, and I can finish off the unicam bit.

@mripard
Copy link
Contributor

mripard commented Jun 10, 2020

I just did that PR here:
#3664

@pelwell
Copy link
Contributor

pelwell commented Jul 10, 2020

What's the status of this?

@naushir
Copy link
Contributor Author

naushir commented Jul 10, 2020

I've made the changes, but still have not tested them. One for next week I'm afraid.

@naushir naushir changed the title Unicam clock changes WIP: nicam clock changes Jul 10, 2020
@naushir
Copy link
Contributor Author

naushir commented Jul 10, 2020

@pelwell, after a rebase, I get a compile error on the dtsi:

Building branch unicam_clock
make[1]: Entering directory '/home/naushir/projects/linux_build/unicam_clock'
  GEN     Makefile
  DTC     arch/arm/boot/dts/bcm2708-rpi-b.dtb
/home/naushir/projects/linux/arch/arm/boot/dts/bcm270x.dtsi:86.22-100.5: ERROR (phandle_references): /soc/csi@7e800000: Reference to non-existent node or label "firmware_clocks"

/home/naushir/projects/linux/arch/arm/boot/dts/bcm270x.dtsi:102.22-116.5: ERROR (phandle_references): /soc/csi@7e801000: Reference to non-existent node or label "firmware_clocks"

  also defined at /home/naushir/projects/linux/arch/arm/boot/dts/bcm283x-rpi-csi1-2lane.dtsi:2.7-4.3
ERROR: Input tree has errors, aborting (use -f to force output)
make[2]: *** [scripts/Makefile.lib:286: arch/arm/boot/dts/bcm2708-rpi-b.dtb] Error 2
make[1]: *** [/home/naushir/projects/linux/Makefile:1249: dtbs] Error 2
make[1]: *** Waiting for unfinished jobs....
make[1]: Leaving directory '/home/naushir/projects/linux_build/unicam_clock'
make: *** [Makefile:179: sub-make] Error 2

I tried adding the following block to the end of the file (which is probably incorrect), but no luck:

&firmware {
	firmware_clocks: clocks {
		compatible = "raspberrypi,firmware-clocks";
		#clock-cells = <1>;
	};
};

My device tree knowledge is very limited, any idea what I should be doing go get firmware_clock recognised again?

@naushir naushir changed the title WIP: nicam clock changes WIP: unicam clock changes Jul 10, 2020
@pelwell
Copy link
Contributor

pelwell commented Jul 10, 2020

It was broken by @mripard's change "clk: rpi: Adjust DT binding to match upstream", and then not fixed by my followup "SQUASH: dts: Fix firmware clocks support" because nothing other than raspberrypi-cpufreq was using firmware_clocks, and raspberrypi-cpufreq isn't using on BCM2835.

Try this patch: pelwell@ab0ef79

@naushir
Copy link
Contributor Author

naushir commented Jul 13, 2020

Building with pelwell/linux@ab0ef79 seems to go further. Will do a run-test shortly. @pelwell, shall I include that patch in this PR, or did you intend to merge that separately?

@pelwell
Copy link
Contributor

pelwell commented Jul 13, 2020

I've merged it to rpi-5.4.y.

When streaming with Unicam, the VPU must have a clock frequency of at
least 250Mhz.  Otherwise, the input fifos could overrun, causing
image corruption.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
@naushir naushir changed the title WIP: unicam clock changes unicam clock changes Jul 13, 2020
@naushir
Copy link
Contributor Author

naushir commented Jul 13, 2020

I think this is doing the right thing now.

@popcornmix
Copy link
Collaborator

Cool - that looks pretty simple. I guess we want to make the vc4 display code use the same mechanism (unrelated to this PR).

Update the documentation to reflect the new "VPU" clock needed
by the bcm2835-unicam driver.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
@pelwell pelwell merged commit 95a969f into raspberrypi:rpi-5.4.y Jul 13, 2020
popcornmix added a commit to raspberrypi/firmware that referenced this pull request Jul 13, 2020
kernel: vc4_hdmi: Support HBR audio
See: raspberrypi/linux#3717

kernel: OV7251 overlay and defconfig
See: raspberrypi/linux#3714

kernel: Imx290 & unicam v4l2-compliance fixes
See: raspberrypi/linux#3712

kernel: Enhances the DAC+ driver to control the optional headphone amplifier
See: raspberrypi/linux#3711

kernel: OV9281 driver and overlay
See: raspberrypi/linux#3709

kernel: dtoverlays: Fixup imx219 and imx477 overlays due to parsing failures
See: raspberrypi/linux#3706

kernel: FKMS: max refresh rate and blocking 1366x768
See: raspberrypi/linux#3704

kernel: Fix lockups and IRQ jitter on multicore RasPis
See: raspberrypi/linux#3703

kernel: dts: Further simplify firmware clocks
See: raspberrypi/linux#3609

kernel: configs: Add CAN_EMS_USB=m
See: raspberrypi/linux#3716

kernel: configs: Enable CONFIG_BLK_DEV_NVME=m

kernel: ARM: dts: Make bcm2711 dts more like 5.7

firmware: arm_loader: Don't enable the ARM USB IRQ
See: raspberrypi/linux#3703

firmware: hdmi: Remove M2MC/BVB min turbo clock request
popcornmix added a commit to Hexxeh/rpi-firmware that referenced this pull request Jul 13, 2020
kernel: vc4_hdmi: Support HBR audio
See: raspberrypi/linux#3717

kernel: OV7251 overlay and defconfig
See: raspberrypi/linux#3714

kernel: Imx290 & unicam v4l2-compliance fixes
See: raspberrypi/linux#3712

kernel: Enhances the DAC+ driver to control the optional headphone amplifier
See: raspberrypi/linux#3711

kernel: OV9281 driver and overlay
See: raspberrypi/linux#3709

kernel: dtoverlays: Fixup imx219 and imx477 overlays due to parsing failures
See: raspberrypi/linux#3706

kernel: FKMS: max refresh rate and blocking 1366x768
See: raspberrypi/linux#3704

kernel: Fix lockups and IRQ jitter on multicore RasPis
See: raspberrypi/linux#3703

kernel: dts: Further simplify firmware clocks
See: raspberrypi/linux#3609

kernel: configs: Add CAN_EMS_USB=m
See: raspberrypi/linux#3716

kernel: configs: Enable CONFIG_BLK_DEV_NVME=m

kernel: ARM: dts: Make bcm2711 dts more like 5.7

firmware: arm_loader: Don't enable the ARM USB IRQ
See: raspberrypi/linux#3703

firmware: hdmi: Remove M2MC/BVB min turbo clock request
@naushir naushir deleted the unicam_clock branch July 13, 2020 13:56
@mripard
Copy link
Contributor

mripard commented Jul 17, 2020

It does in mainline already, @6by9 wanted to do a merge soon, so I guess it will come to 5.4 as well

popcornmix added a commit to raspberrypi/firmware that referenced this pull request Jul 17, 2020
See: raspberrypi/linux#3730

kernel: drm/vc4: Add DRM_MODE_FLAG_DBLCLK support to vc4-fkms
See: raspberrypi/linux#3725

kernel: raspberrypi: Report sensor orientation through DT
See: raspberrypi/linux#3723

kernel: correct SND_SOC_DAILINK_DEFS
See: raspberrypi/linux#3722

kernel: ARM: dts: Select the actpwr LED trigger on Zeroes

kernel: staging: vc04_services: isp: Rework lens shading to take a dmabuf
See: raspberrypi/linux#3715

kernel: unicam clock changes
See: raspberrypi/linux#3609

firmware: IL: camera: Fix stereoscopic pool allocations

firmware: arm_loader: Add support for double clock/pixel_rep for FKMS
See: raspberrypi/linux#3725

firmware: scalerlib: Set the default chroma location for YUV10 to match 8bit
firmware: scalerlib: Set chroma_vrep correctly for YUV10COL

firmware: isp: check the hi-res resize filter mode when the input crop changes
popcornmix added a commit to Hexxeh/rpi-firmware that referenced this pull request Jul 17, 2020
See: raspberrypi/linux#3730

kernel: drm/vc4: Add DRM_MODE_FLAG_DBLCLK support to vc4-fkms
See: raspberrypi/linux#3725

kernel: raspberrypi: Report sensor orientation through DT
See: raspberrypi/linux#3723

kernel: correct SND_SOC_DAILINK_DEFS
See: raspberrypi/linux#3722

kernel: ARM: dts: Select the actpwr LED trigger on Zeroes

kernel: staging: vc04_services: isp: Rework lens shading to take a dmabuf
See: raspberrypi/linux#3715

kernel: unicam clock changes
See: raspberrypi/linux#3609

firmware: IL: camera: Fix stereoscopic pool allocations

firmware: arm_loader: Add support for double clock/pixel_rep for FKMS
See: raspberrypi/linux#3725

firmware: scalerlib: Set the default chroma location for YUV10 to match 8bit
firmware: scalerlib: Set chroma_vrep correctly for YUV10COL

firmware: isp: check the hi-res resize filter mode when the input crop changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants