From: Maxime Ripard <hidden> Date: 2017-05-03 12:00:18
Hi,
Here is an attempt at getting the HDMI controller running.
This HDMI controller is found on a number of old Allwinner SoCs (A10, A10s,
A20, A31).
This driver only supports for now the A10s because it was an easy target,
being very close to the A13 that is already supported by our DRM driver.
There's nothing out of the extraordinary there, except maybe the clock
setup. All the internal clocks (TMDS, DDC) have been modeled using the
common clock framework, the TMDS clock being the parent of the DDC one.
While this might sound overkill, other SoC have a different, external
source for the DDC clock, which will be easier to support through the clock
framework.
The IP also supports audio (through an already supported i2s controller,
and some missing configuration in the HDMI controller) and CEC. Both will
come eventually.
Let me know what you think!
Maxime
Changes from v1:
- Fixed typos in the CCU header and the HDMI code
- Reintroduced the comment for the backporch timings
- Renamed the hdmi node to hdmi, instead of hdmi0
- Added support for hdmi-connector
- Added a separate Kconfig option for the HDMI support
- Changed the TCON muxing configuration for an explicit call in the
TCON's "clients"
- Fixed the initialisation sequence that was clearing the clocks bits
- Constified the HDMI's structures and removed whitespaces errors
- Fixed an issue in the sunxi-ng code that was not reporting the proper
parent clock rate if it was modified
- Removed unused headers
- Removed CLK_SET_RATE_PARENT for the DDC clock
- Used the DDC address defines
- Removed the interlace flag that wasn't supported at the moment
- Moved most of the HDMI encoder init to the bind function like we do for
the other encoders
- Switched to drm_of_find_possible_crtcs
- Removed the extra printk that were still in my code
- Rebased on top of linux-next
- Removed the patch changing the divider_round_rate prototype to
introduce a new function instead that takes the parent clock to
evaluate
- Added a clk_set_rate for the hdmi module clock
- Fixed the V_TOTAL TCON ch0 calculation to be consistent with ch1's
- Defined all registers, and remove the TODOs
- Fixed the EDID issues by increasing the timeout.
- Added an atomic_check to prevent the DBLCLK modes to be used, as it is
not supported yet
- Updated the binding to add the interrupts and DMA channels
Maxime Ripard (20):
clk: divider: Make divider_round_rate take the parent clock
clk: sunxi-ng: Pass the parent and a pointer to the clocks round rate
clk: sunxi-ng: div: Switch to divider_round_rate
clk: sunxi-ng: mux: Don't just rely on the parent for CLK_SET_RATE_PARENT
clk: sunxi-ng: mux: split out the pre-divider computation code
clk: sunxi-ng: mux: Change pre-divider application function prototype
clk: sunxi-ng: mux: Re-adjust parent rate
clk: sunxi-ng: sun5i: Export video PLLs
drm/sun4i: tcon: Add channel debug
drm/sun4i: tcon: Move the muxing out of the mode set function
drm/sun4i: tcon: Switch mux on only for composite
drm/sun4i: tcon: Fix tcon channel 1 backporch calculation
drm/sun4i: tcon: Change vertical total size computation inconsistency
drm/sun4i: tcon: multiply the vtotal when not in interlace
drm/sun4i: Ignore the generic connectors for components
dt-bindings: display: sun4i: Add HDMI display bindings
dt-bindings: display: sun4i: Add allwinner,tcon-channel property
drm/sun4i: Add HDMI support
ARM: sun5i: a10s: Add the HDMI controller node
ARM: sun5i: a10s-olinuxino: Enable HDMI
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 90 +-
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 29 +-
arch/arm/boot/dts/sun5i-a10s.dtsi | 50 +-
arch/arm/boot/dts/sun5i.dtsi | 1 +-
drivers/clk/clk-divider.c | 26 +-
drivers/clk/sunxi-ng/ccu-sun5i.h | 6 +-
drivers/clk/sunxi-ng/ccu_div.c | 38 +-
drivers/clk/sunxi-ng/ccu_mp.c | 15 +-
drivers/clk/sunxi-ng/ccu_mult.c | 19 +-
drivers/clk/sunxi-ng/ccu_mux.c | 90 +-
drivers/clk/sunxi-ng/ccu_mux.h | 11 +-
drivers/clk/sunxi-ng/ccu_nkm.c | 7 +-
drivers/gpu/drm/sun4i/Kconfig | 9 +-
drivers/gpu/drm/sun4i/Makefile | 6 +-
drivers/gpu/drm/sun4i/sun4i_drv.c | 8 +-
drivers/gpu/drm/sun4i/sun4i_hdmi.h | 157 ++-
drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 127 ++-
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 493 +++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 225 +++-
drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 +-
drivers/gpu/drm/sun4i/sun4i_tcon.c | 43 +-
drivers/gpu/drm/sun4i/sun4i_tcon.h | 4 +-
drivers/gpu/drm/sun4i/sun4i_tv.c | 1 +-
include/dt-bindings/clock/sun5i-ccu.h | 3 +-
include/linux/clk-provider.h | 4 +-
25 files changed, 1347 insertions(+), 116 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
base-commit: ee91aaf669ac4fbb09283958f69d57dcad5c4963
--
git-series 0.8.11
From: Maxime Ripard <hidden> Date: 2017-05-03 12:00:28
So far, divider_round_rate only considers the parent clock returned by
clk_hw_get_parent.
This works fine on clocks that have a single parents, this doesn't work on
muxes, since we will only consider the first parent, while other parents
may totally be able to provide a better combination.
Clocks in that case cannot use divider_round_rate, so would have to come up
with a very similar logic to work around it. Instead of having to do
something like this, and duplicate that logic everywhere, create a
divider_round_rate parent to allow caller to give an additional parameter
for the parent clock to consider.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/clk-divider.c | 26 ++++++++++++++++++--------
include/linux/clk-provider.h | 4 ++++
2 files changed, 22 insertions(+), 8 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:00:36
The clocks might need to modify their parent clocks. In order to make that
possible, give them access to the parent clock being evaluated, and to a
pointer to the parent rate so that they can modify it if needed.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_div.c | 7 ++++---
drivers/clk/sunxi-ng/ccu_mp.c | 7 ++++---
drivers/clk/sunxi-ng/ccu_mult.c | 11 ++++++-----
drivers/clk/sunxi-ng/ccu_mux.c | 8 +++++---
drivers/clk/sunxi-ng/ccu_mux.h | 3 ++-
drivers/clk/sunxi-ng/ccu_nkm.c | 7 ++++---
6 files changed, 25 insertions(+), 18 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:00:44
divider_round_rate already evaluates changing the parent rate if
CLK_SET_RATE_PARENT is set. Now that we can do that on muxes too, let's
just use it.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_div.c | 27 ++++-----------------------
1 file changed, 4 insertions(+), 23 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:00:55
The pre-divider retrieval code was merged into the function to apply the
current pre-divider onto the parent clock rate so that we can use that
adjusted value to do our factors computation.
However, since we'll need to do the reverse operation, we need to split out
that code into a function that will be shared.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_mux.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:07
The current code only rely on the parent to change its rate in the case
where CLK_SET_RATE_PARENT is set.
However, some clock rates might be obtained only through a modification of
the parent and the clock divider. Just rely on the round rate of the clocks
to give us the best computation that might be achieved for a given rate.
round_rate functions now need to honor CLK_SET_RATE_PARENT, but either the
functions already do that if they modify the parent, or don't modify the
praents at all.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_mux.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:18
The video PLLs are used directly by the HDMI controller. Export them so
that we can use them in our DT node.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu-sun5i.h | 6 ++++--
include/dt-bindings/clock/sun5i-ccu.h | 3 +++
2 files changed, 7 insertions(+), 2 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:26
The Allwinner Timings Controller has two, mutually exclusive, channels.
When the binding has been introduced, it was assumed that there would be
only a single user per channel in the system.
While this is likely for the channel 0 which only connects to LCD displays,
it turns out that the channel 1 can be connected to multiple controllers in
the SoC (HDMI and TV encoders for example). And while the simultaneous use
of HDMI and TV outputs cannot be achieved, switching from one to the other
at runtime definitely sounds plausible.
Add an extra property, allwinner,tcon-channel, to specify for a given
endpoint which TCON channel it is connected to, while falling back to the
previous mechanism if that property is missing.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 11 ++++---
1 file changed, 7 insertions(+), 4 deletions(-)
@@ -75,10 +75,13 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint, the second one the output- The output should have two endpoints. The first is the block- connected to the TCON channel 0 (usually a panel or a bridge), the- second the block connected to the TCON channel 1 (usually the TV- encoder)+ The output may have multiple endpoints. The TCON has two channels,+ usually with the first channel being used for the panels interfaces+ (RGB, LVDS, etc.), and the second being used for the outputs that+ require another controller (TV Encoder, HDMI, etc.). The endpoints+ will take an extra property, allwinner,tcon-channel, to specify the+ channel the endpoint is associated to. If that property is not+ present, the endpoint number will be used as the channel number. On SoCs other than the A33, there is one more clock required: - 'tcon-ch1': The clock driving the TCON channel 1
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:30
The A10s Olinuxino has an HDMI connector. Make sure we can use it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 29 +++++++++++++++++-
1 file changed, 29 insertions(+), 0 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:32
Even though that mux is undocumented, it seems like it needs to be set to 1
when using composite, and 0 when using HDMI.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:33
It seems like what's called a backporch in the datasheet is actually the
backporch plus the sync period. Fix that in our driver.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:36
Currently, the parent rate given back to the clock framework in our
request is the original parent rate we calculated before trying to round
the rate of our clock.
This works fine unless our clock also changes its parent rate, in which
case we will simply ignore that change and still use the previous parent
rate.
Create a new function to re-adjust the parent rate to take the pre-dividers
into account, and give that back to the clock framework.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_mux.c | 33 ++++++++++++++++++++++++++++-----
1 file changed, 28 insertions(+), 5 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:40
The A10s has an HDMI controller connected to the second TCON channel. Add
it to our DT.
Since the TV Encoder was the only channel 1 user so far, also add the
property now that we have several users.
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 50 ++++++++++++++++++++++++++++++++-
arch/arm/boot/dts/sun5i.dtsi | 1 +-
2 files changed, 51 insertions(+), 0 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:48
Both TCON channels need to have the resolution doubled, since the size the
hardware is going to use is whatever we put in the register divided by two.
However, we handle it differently for the two channels: in the channel 0,
our register access macro does the multiplication of the value passed as
paremeter, while in the channel 1, the macro doesn't do this, and we need
to do it before calling it.
Make this consistent by aligning the channel 0 with the channel 1
behaviour.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 +-
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:01:59
The earlier Allwinner SoCs (A10, A10s, A20, A31) have an embedded HDMI
controller.
That HDMI controller is able to do audio and CEC, but those have been left
out for now.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/Kconfig | 9 +-
drivers/gpu/drm/sun4i/Makefile | 6 +-
drivers/gpu/drm/sun4i/sun4i_hdmi.h | 157 +++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 127 +++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 493 +++++++++++++++++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 225 ++++++++++-
6 files changed, 1017 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -0,0 +1,493 @@+/*+*Copyright(C)2016MaximeRipard+*+*MaximeRipard<maxime.ripard@free-electrons.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicenseas+*publishedbytheFreeSoftwareFoundation;eitherversion2of+*theLicense,or(atyouroption)anylaterversion.+*/++#include<drm/drmP.h>+#include<drm/drm_atomic_helper.h>+#include<drm/drm_crtc_helper.h>+#include<drm/drm_edid.h>+#include<drm/drm_encoder.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>++#include<linux/clk.h>+#include<linux/component.h>+#include<linux/iopoll.h>+#include<linux/platform_device.h>+#include<linux/pm_runtime.h>++#include"sun4i_backend.h"+#include"sun4i_drv.h"+#include"sun4i_hdmi.h"+#include"sun4i_tcon.h"++#define DDC_SEGMENT_ADDR 0x30++staticinlinestructsun4i_hdmi*+drm_encoder_to_sun4i_hdmi(structdrm_encoder*encoder)+{+returncontainer_of(encoder,structsun4i_hdmi,+encoder);+}++staticinlinestructsun4i_hdmi*+drm_connector_to_sun4i_hdmi(structdrm_connector*connector)+{+returncontainer_of(connector,structsun4i_hdmi,+connector);+}++staticintsun4i_hdmi_setup_avi_infoframes(structsun4i_hdmi*hdmi,+structdrm_display_mode*mode)+{+structhdmi_avi_infoframeframe;+u8buffer[17];+inti,ret;++ret=drm_hdmi_avi_infoframe_from_display_mode(&frame,mode);+if(ret<0){+DRM_ERROR("Failed to get infoframes from mode\n");+returnret;+}++ret=hdmi_avi_infoframe_pack(&frame,buffer,sizeof(buffer));+if(ret<0){+DRM_ERROR("Failed to pack infoframes\n");+returnret;+}++for(i=0;i<sizeof(buffer);i++)+writeb(buffer[i],hdmi->base+SUN4I_HDMI_AVI_INFOFRAME_REG(i));++return0;+}++staticintsun4i_hdmi_atomic_check(structdrm_encoder*encoder,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structdrm_display_mode*mode=&crtc_state->mode;++if(mode->flags&DRM_MODE_FLAG_DBLCLK)+return-EINVAL;++return0;+}++staticvoidsun4i_hdmi_disable(structdrm_encoder*encoder)+{+structsun4i_hdmi*hdmi=drm_encoder_to_sun4i_hdmi(encoder);+structsun4i_drv*drv=hdmi->drv;+structsun4i_tcon*tcon=drv->tcon;+u32val;++DRM_DEBUG_DRIVER("Disabling the HDMI Output\n");++val=readl(hdmi->base+SUN4I_HDMI_VID_CTRL_REG);+val&=~SUN4I_HDMI_VID_CTRL_ENABLE;+writel(val,hdmi->base+SUN4I_HDMI_VID_CTRL_REG);++sun4i_tcon_channel_disable(tcon,1);+}++staticvoidsun4i_hdmi_enable(structdrm_encoder*encoder)+{+structdrm_display_mode*mode=&encoder->crtc->state->adjusted_mode;+structsun4i_hdmi*hdmi=drm_encoder_to_sun4i_hdmi(encoder);+structsun4i_drv*drv=hdmi->drv;+structsun4i_tcon*tcon=drv->tcon;+u32val=0;++DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");++sun4i_tcon_channel_enable(tcon,1);++sun4i_hdmi_setup_avi_infoframes(hdmi,mode);+val|=SUN4I_HDMI_PKT_CTRL_TYPE(0,SUN4I_HDMI_PKT_AVI);+val|=SUN4I_HDMI_PKT_CTRL_TYPE(1,SUN4I_HDMI_PKT_END);+writel(val,hdmi->base+SUN4I_HDMI_PKT_CTRL_REG(0));++val=SUN4I_HDMI_VID_CTRL_ENABLE;+if(hdmi->hdmi_monitor)+val|=SUN4I_HDMI_VID_CTRL_HDMI_MODE;++writel(val,hdmi->base+SUN4I_HDMI_VID_CTRL_REG);+}++staticvoidsun4i_hdmi_mode_set(structdrm_encoder*encoder,+structdrm_display_mode*mode,+structdrm_display_mode*adjusted_mode)+{+structsun4i_hdmi*hdmi=drm_encoder_to_sun4i_hdmi(encoder);+structsun4i_drv*drv=hdmi->drv;+structsun4i_tcon*tcon=drv->tcon;+unsignedintx,y;+u32val;++sun4i_tcon1_mode_set(tcon,mode);+sun4i_tcon_set_mux(tcon,1,encoder);++clk_set_rate(tcon->sclk1,mode->crtc_clock*1000);+clk_set_rate(hdmi->mod_clk,mode->crtc_clock*1000);+clk_set_rate(hdmi->tmds_clk,mode->crtc_clock*1000);++/* Set input sync enable */+writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC,+hdmi->base+SUN4I_HDMI_UNKNOWN_REG);++/* Setup timing registers */+writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay)|+SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay),+hdmi->base+SUN4I_HDMI_VID_TIMING_ACT_REG);++x=mode->htotal-mode->hsync_start;+y=mode->vtotal-mode->vsync_start;+writel(SUN4I_HDMI_VID_TIMING_X(x)|SUN4I_HDMI_VID_TIMING_Y(y),+hdmi->base+SUN4I_HDMI_VID_TIMING_BP_REG);++x=mode->hsync_start-mode->hdisplay;+y=mode->vsync_start-mode->vdisplay;+writel(SUN4I_HDMI_VID_TIMING_X(x)|SUN4I_HDMI_VID_TIMING_Y(y),+hdmi->base+SUN4I_HDMI_VID_TIMING_FP_REG);++x=mode->hsync_end-mode->hsync_start;+y=mode->vsync_end-mode->vsync_start;+writel(SUN4I_HDMI_VID_TIMING_X(x)|SUN4I_HDMI_VID_TIMING_Y(y),+hdmi->base+SUN4I_HDMI_VID_TIMING_SPW_REG);++val=SUN4I_HDMI_VID_TIMING_POL_TX_CLK;+if(mode->flags&DRM_MODE_FLAG_PHSYNC)+val|=SUN4I_HDMI_VID_TIMING_POL_HSYNC;++if(mode->flags&DRM_MODE_FLAG_PVSYNC)+val|=SUN4I_HDMI_VID_TIMING_POL_VSYNC;++writel(val,hdmi->base+SUN4I_HDMI_VID_TIMING_POL_REG);+}++staticconststructdrm_encoder_helper_funcssun4i_hdmi_helper_funcs={+.atomic_check=sun4i_hdmi_atomic_check,+.disable=sun4i_hdmi_disable,+.enable=sun4i_hdmi_enable,+.mode_set=sun4i_hdmi_mode_set,+};++staticconststructdrm_encoder_funcssun4i_hdmi_funcs={+.destroy=drm_encoder_cleanup,+};++staticintsun4i_hdmi_read_sub_block(structsun4i_hdmi*hdmi,+unsignedintblk,unsignedintoffset,+u8*buf,unsignedintcount)+{+unsignedlongreg;+inti;++reg=readl(hdmi->base+SUN4I_HDMI_DDC_CTRL_REG);+reg&=~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;+writel(reg|SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ,+hdmi->base+SUN4I_HDMI_DDC_CTRL_REG);++writel(SUN4I_HDMI_DDC_ADDR_SEGMENT(offset>>8)|+SUN4I_HDMI_DDC_ADDR_EDDC(DDC_SEGMENT_ADDR<<1)|+SUN4I_HDMI_DDC_ADDR_OFFSET(offset)|+SUN4I_HDMI_DDC_ADDR_SLAVE(DDC_ADDR),+hdmi->base+SUN4I_HDMI_DDC_ADDR_REG);++reg=readl(hdmi->base+SUN4I_HDMI_DDC_FIFO_CTRL_REG);+writel(reg|SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR,+hdmi->base+SUN4I_HDMI_DDC_FIFO_CTRL_REG);++writel(count,hdmi->base+SUN4I_HDMI_DDC_BYTE_COUNT_REG);+writel(SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ,+hdmi->base+SUN4I_HDMI_DDC_CMD_REG);++reg=readl(hdmi->base+SUN4I_HDMI_DDC_CTRL_REG);+writel(reg|SUN4I_HDMI_DDC_CTRL_START_CMD,+hdmi->base+SUN4I_HDMI_DDC_CTRL_REG);++if(readl_poll_timeout(hdmi->base+SUN4I_HDMI_DDC_CTRL_REG,reg,+!(reg&SUN4I_HDMI_DDC_CTRL_START_CMD),+100,100000))+return-EIO;++for(i=0;i<count;i++)+buf[i]=readb(hdmi->base+SUN4I_HDMI_DDC_FIFO_DATA_REG);++return0;+}++staticintsun4i_hdmi_read_edid_block(void*data,u8*buf,unsignedintblk,+size_tlength)+{+structsun4i_hdmi*hdmi=data;+intretry=2,i;++do{+for(i=0;i<length;i+=SUN4I_HDMI_DDC_FIFO_SIZE){+unsignedcharoffset=blk*EDID_LENGTH+i;+unsignedintcount=min((unsignedint)SUN4I_HDMI_DDC_FIFO_SIZE,+length-i);+intret;++ret=sun4i_hdmi_read_sub_block(hdmi,blk,offset,+buf+i,count);+if(ret)+returnret;+}+}while(!drm_edid_block_valid(buf,blk,true,NULL)&&(retry--));++return0;+}++staticintsun4i_hdmi_get_modes(structdrm_connector*connector)+{+structsun4i_hdmi*hdmi=drm_connector_to_sun4i_hdmi(connector);+unsignedlongreg;+structedid*edid;+intret;++/* Reset i2c controller */+writel(SUN4I_HDMI_DDC_CTRL_ENABLE|SUN4I_HDMI_DDC_CTRL_RESET,+hdmi->base+SUN4I_HDMI_DDC_CTRL_REG);+if(readl_poll_timeout(hdmi->base+SUN4I_HDMI_DDC_CTRL_REG,reg,+!(reg&SUN4I_HDMI_DDC_CTRL_RESET),+100,2000))+return-EIO;++writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE|+SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,+hdmi->base+SUN4I_HDMI_DDC_LINE_CTRL_REG);++clk_set_rate(hdmi->ddc_clk,100000);++edid=drm_do_get_edid(connector,sun4i_hdmi_read_edid_block,hdmi);+if(!edid)+return0;++hdmi->hdmi_monitor=drm_detect_hdmi_monitor(edid);+DRM_DEBUG_DRIVER("Monitor is %s monitor\n",+hdmi->hdmi_monitor?"an HDMI":"a DVI");++drm_mode_connector_update_edid_property(connector,edid);+ret=drm_add_edid_modes(connector,edid);+kfree(edid);++returnret;+}++staticconststructdrm_connector_helper_funcssun4i_hdmi_connector_helper_funcs={+.get_modes=sun4i_hdmi_get_modes,+};++staticenumdrm_connector_status+sun4i_hdmi_connector_detect(structdrm_connector*connector,boolforce)+{+structsun4i_hdmi*hdmi=drm_connector_to_sun4i_hdmi(connector);+unsignedlongreg;++if(readl_poll_timeout(hdmi->base+SUN4I_HDMI_HPD_REG,reg,+reg&SUN4I_HDMI_HPD_HIGH,+0,500000))+returnconnector_status_disconnected;++returnconnector_status_connected;+}++staticconststructdrm_connector_funcssun4i_hdmi_connector_funcs={+.dpms=drm_atomic_helper_connector_dpms,+.detect=sun4i_hdmi_connector_detect,+.fill_modes=drm_helper_probe_single_connector_modes,+.destroy=drm_connector_cleanup,+.reset=drm_atomic_helper_connector_reset,+.atomic_duplicate_state=drm_atomic_helper_connector_duplicate_state,+.atomic_destroy_state=drm_atomic_helper_connector_destroy_state,+};++staticintsun4i_hdmi_bind(structdevice*dev,structdevice*master,+void*data)+{+structplatform_device*pdev=to_platform_device(dev);+structdrm_device*drm=data;+structsun4i_drv*drv=drm->dev_private;+structsun4i_hdmi*hdmi;+structresource*res;+u32reg;+intret;++hdmi=devm_kzalloc(dev,sizeof(*hdmi),GFP_KERNEL);+if(!hdmi)+return-ENOMEM;+dev_set_drvdata(dev,hdmi);+hdmi->dev=dev;+hdmi->drv=drv;++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+hdmi->base=devm_ioremap_resource(dev,res);+if(IS_ERR(hdmi->base)){+dev_err(dev,"Couldn't map the HDMI encoder registers\n");+returnPTR_ERR(hdmi->base);+}++hdmi->bus_clk=devm_clk_get(dev,"ahb");+if(IS_ERR(hdmi->bus_clk)){+dev_err(dev,"Couldn't get the HDMI bus clock\n");+returnPTR_ERR(hdmi->bus_clk);+}+clk_prepare_enable(hdmi->bus_clk);++hdmi->mod_clk=devm_clk_get(dev,"mod");+if(IS_ERR(hdmi->mod_clk)){+dev_err(dev,"Couldn't get the HDMI mod clock\n");+returnPTR_ERR(hdmi->mod_clk);+}+clk_prepare_enable(hdmi->mod_clk);++hdmi->pll0_clk=devm_clk_get(dev,"pll-0");+if(IS_ERR(hdmi->pll0_clk)){+dev_err(dev,"Couldn't get the HDMI PLL 0 clock\n");+returnPTR_ERR(hdmi->pll0_clk);+}++hdmi->pll1_clk=devm_clk_get(dev,"pll-1");+if(IS_ERR(hdmi->pll1_clk)){+dev_err(dev,"Couldn't get the HDMI PLL 1 clock\n");+returnPTR_ERR(hdmi->pll1_clk);+}++ret=sun4i_tmds_create(hdmi);+if(ret){+dev_err(dev,"Couldn't create the TMDS clock\n");+returnret;+}++writel(SUN4I_HDMI_CTRL_ENABLE,hdmi->base+SUN4I_HDMI_CTRL_REG);++writel(SUN4I_HDMI_PAD_CTRL0_TXEN|SUN4I_HDMI_PAD_CTRL0_CKEN|+SUN4I_HDMI_PAD_CTRL0_PWENG|SUN4I_HDMI_PAD_CTRL0_PWEND|+SUN4I_HDMI_PAD_CTRL0_PWENC|SUN4I_HDMI_PAD_CTRL0_LDODEN|+SUN4I_HDMI_PAD_CTRL0_LDOCEN|SUN4I_HDMI_PAD_CTRL0_BIASEN,+hdmi->base+SUN4I_HDMI_PAD_CTRL0_REG);++/*+*Wecan'tjustinitializetheregisterthere,weneedto+*protecttheclockbitsthathavealreadybeenreadoutand+*cachedbytheclockframework.+*/+reg=readl(hdmi->base+SUN4I_HDMI_PAD_CTRL1_REG);+reg=reg&SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;+reg|=SUN4I_HDMI_PAD_CTRL1_REG_AMP(6)|+SUN4I_HDMI_PAD_CTRL1_REG_EMP(2)|+SUN4I_HDMI_PAD_CTRL1_REG_DENCK|+SUN4I_HDMI_PAD_CTRL1_REG_DEN|+SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT|+SUN4I_HDMI_PAD_CTRL1_EMP_OPT|+SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT|+SUN4I_HDMI_PAD_CTRL1_AMP_OPT;+writel(reg,hdmi->base+SUN4I_HDMI_PAD_CTRL1_REG);++reg=readl(hdmi->base+SUN4I_HDMI_PLL_CTRL_REG);+reg=reg&SUN4I_HDMI_PLL_CTRL_DIV_MASK;+reg|=SUN4I_HDMI_PLL_CTRL_VCO_S(8)|SUN4I_HDMI_PLL_CTRL_CS(7)|+SUN4I_HDMI_PLL_CTRL_CP_S(239)|SUN4I_HDMI_PLL_CTRL_S(7)|+SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4)|SUN4I_HDMI_PLL_CTRL_SDIV2|+SUN4I_HDMI_PLL_CTRL_LDO2_EN|SUN4I_HDMI_PLL_CTRL_LDO1_EN|+SUN4I_HDMI_PLL_CTRL_HV_IS_33|SUN4I_HDMI_PLL_CTRL_BWS|+SUN4I_HDMI_PLL_CTRL_PLL_EN;+writel(reg,hdmi->base+SUN4I_HDMI_PLL_CTRL_REG);++ret=sun4i_ddc_create(hdmi,hdmi->tmds_clk);+if(ret){+dev_err(dev,"Couldn't create the DDC clock\n");+returnret;+}++drm_encoder_helper_add(&hdmi->encoder,+&sun4i_hdmi_helper_funcs);+ret=drm_encoder_init(drm,+&hdmi->encoder,+&sun4i_hdmi_funcs,+DRM_MODE_ENCODER_TMDS,+NULL);+if(ret){+dev_err(dev,"Couldn't initialise the HDMI encoder\n");+returnret;+}++hdmi->encoder.possible_crtcs=drm_of_find_possible_crtcs(drm,+dev->of_node);+if(!hdmi->encoder.possible_crtcs)+returnret;++drm_connector_helper_add(&hdmi->connector,+&sun4i_hdmi_connector_helper_funcs);+ret=drm_connector_init(drm,&hdmi->connector,+&sun4i_hdmi_connector_funcs,+DRM_MODE_CONNECTOR_HDMIA);+if(ret){+dev_err(dev,+"Couldn't initialise the HDMI connector\n");+gotoerr_cleanup_connector;+}++drm_mode_connector_attach_encoder(&hdmi->connector,&hdmi->encoder);++return0;++err_cleanup_connector:+drm_encoder_cleanup(&hdmi->encoder);+returnret;+}++staticvoidsun4i_hdmi_unbind(structdevice*dev,structdevice*master,+void*data)+{+structsun4i_hdmi*hdmi=dev_get_drvdata(dev);++drm_connector_cleanup(&hdmi->connector);+drm_encoder_cleanup(&hdmi->encoder);+}++staticconststructcomponent_opssun4i_hdmi_ops={+.bind=sun4i_hdmi_bind,+.unbind=sun4i_hdmi_unbind,+};++staticintsun4i_hdmi_probe(structplatform_device*pdev)+{+returncomponent_add(&pdev->dev,&sun4i_hdmi_ops);+}++staticintsun4i_hdmi_remove(structplatform_device*pdev)+{+component_del(&pdev->dev,&sun4i_hdmi_ops);++return0;+}++staticconststructof_device_idsun4i_hdmi_of_table[]={+{.compatible="allwinner,sun5i-a10s-hdmi"},+{}+};+MODULE_DEVICE_TABLE(of,sun4i_hdmi_of_table);++staticstructplatform_driversun4i_hdmi_driver={+.probe=sun4i_hdmi_probe,+.remove=sun4i_hdmi_remove,+.driver={+.name="sun4i-hdmi",+.of_match_table=sun4i_hdmi_of_table,+},+};+module_platform_driver(sun4i_hdmi_driver);++MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");+MODULE_DESCRIPTION("Allwinner A10 HDMI Driver");+MODULE_LICENSE("GPL");
From: Maxime Ripard <hidden> Date: 2017-05-03 12:02:11
The muxing can actually happen on both channels on some SoCs, so it makes
more sense to just move it out of the sun4i_tcon1_mode_set function and
create a separate function that needs to be called by the encoders.
Let's do that and convert the existing drivers.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 +
drivers/gpu/drm/sun4i/sun4i_tcon.c | 22 ++++++++++++++++------
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 ++
drivers/gpu/drm/sun4i/sun4i_tv.c | 1 +
4 files changed, 20 insertions(+), 6 deletions(-)
@@ -393,6 +393,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,conststructtv_mode*tv_mode=sun4i_tv_find_tv_by_mode(mode);sun4i_tcon1_mode_set(tcon,mode);+sun4i_tcon_set_mux(tcon,1,encoder);/* Enable and map the DAC to the output */regmap_update_bits(tv->regs,SUN4I_TVE_EN_REG,
From: Maxime Ripard <hidden> Date: 2017-05-03 12:02:20
The generic connectors such as hdmi-connector doesn't have any driver in,
so if they are added to the component list, we will be waiting forever for
a non-existing driver to probe.
Add a list of the connectors we want to ignore when building our component
list.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_drv.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:02:31
One of the possible output of the display pipeline, on the SoCs that have
it, is the HDMI controller.
Add a binding for it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 79 +++++++-
1 file changed, 79 insertions(+), 0 deletions(-)
@@ -4,6 +4,34 @@ Allwinner A10 Display Pipeline The Allwinner A10 Display pipeline is composed of several components that are going to be documented below:+HDMI Encoder+------------++The HDMI Encoder supports the HDMI video and audio outputs, and does+CEC. It is one end of the pipeline.++Required properties:+ - compatible: value must be one of:+ * allwinner,sun5i-a10s-hdmi+ - reg: base address and size of memory-mapped region+ - interrupts: interrupt associated to this IP+ - clocks: phandles to the clocks feeding the HDMI encoder+ * ahb: the HDMI interface clock+ * mod: the HDMI module clock+ * pll-0: the first video PLL+ * pll-1: the second video PLL+ - clock-names: the clock names mentioned above+ - dmas: phandles to the DMA channels used by the HDMI encoder+ * ddc-tx: The channel for DDC transmission+ * ddc-rx: The channel for DDC reception+ * audio-tx: The channel used for audio transmission+ - dma-names: the channel names mentioned above++ - ports: A ports node with endpoint definitions as defined in+ Documentation/devicetree/bindings/media/video-interfaces.txt. The+ first port should be the input endpoint. The second should be the+ output, usually to an HDMI connector.+ TV Encoder ----------
From: Maxime Ripard <hidden> Date: 2017-05-03 12:02:42
It appears that the total vertical resolution needs to be doubled when
we're not in interlaced. Make sure that is the case.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:02:50
While all functions have debug logs, the channel enable and disable are not
logged. Make sure this is the case.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++++
1 file changed, 4 insertions(+), 0 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-03 12:05:32
The current function name is a bit confusing, and doesn't really allow to
create an explicit function to reverse the operation.
We also for now change the parent rate through a pointer, while we don't
return anything.
In order to be less confusing, and easier to use for downstream users,
change the function name to something hopefully clearer, and return the
adjusted rate instead of changing the pointer.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_div.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mp.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mult.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mux.c | 29 ++++++++++++-----------------
drivers/clk/sunxi-ng/ccu_mux.h | 8 ++++----
5 files changed, 28 insertions(+), 33 deletions(-)
@@ -87,8 +87,8 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,u32reg;/* Adjust parent_rate according to pre-dividers */-ccu_mux_helper_adjust_parent_for_prediv(&cmp->common,&cmp->mux,--1,&parent_rate);+parent_rate=ccu_mux_helper_apply_prediv(&cmp->common,&cmp->mux,-1,+parent_rate);reg=readl(cmp->common.base+cmp->common.reg);
@@ -123,8 +123,8 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,u32reg;/* Adjust parent_rate according to pre-dividers */-ccu_mux_helper_adjust_parent_for_prediv(&cmp->common,&cmp->mux,--1,&parent_rate);+parent_rate=ccu_mux_helper_apply_prediv(&cmp->common,&cmp->mux,-1,+parent_rate);max_m=cmp->m.max?:1<<cmp->m.width;max_p=cmp->p.max?:1<<((1<<cmp->p.width)-1);
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
So far, divider_round_rate only considers the parent clock returned by
clk_hw_get_parent.
This works fine on clocks that have a single parents, this doesn't work on
muxes, since we will only consider the first parent, while other parents
may totally be able to provide a better combination.
Clocks in that case cannot use divider_round_rate, so would have to come up
with a very similar logic to work around it. Instead of having to do
something like this, and duplicate that logic everywhere, create a
divider_round_rate parent to allow caller to give an additional parameter
for the parent clock to consider.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/clk-divider.c | 26 ++++++++++++++++++--------
include/linux/clk-provider.h | 4 ++++
2 files changed, 22 insertions(+), 8 deletions(-)
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
The clocks might need to modify their parent clocks. In order to make that
possible, give them access to the parent clock being evaluated, and to a
pointer to the parent rate so that they can modify it if needed.
Signed-off-by: Maxime Ripard <redacted>
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
The current code only rely on the parent to change its rate in the case
where CLK_SET_RATE_PARENT is set.
However, some clock rates might be obtained only through a modification of
the parent and the clock divider. Just rely on the round rate of the clocks
to give us the best computation that might be achieved for a given rate.
round_rate functions now need to honor CLK_SET_RATE_PARENT, but either the
functions already do that if they modify the parent, or don't modify the
praents at all.
Signed-off-by: Maxime Ripard <redacted>
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
The pre-divider retrieval code was merged into the function to apply the
current pre-divider onto the parent clock rate so that we can use that
adjusted value to do our factors computation.
However, since we'll need to do the reverse operation, we need to split out
that code into a function that will be shared.
Signed-off-by: Maxime Ripard <redacted>
Acked-by: Chen-Yu Tsai <redacted>
Seems this would conflict with my "clk: sunxi-ng: Support multiple variable
pre-dividers" patch though. We'll see how this works out.
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
The current function name is a bit confusing, and doesn't really allow to
create an explicit function to reverse the operation.
We also for now change the parent rate through a pointer, while we don't
return anything.
In order to be less confusing, and easier to use for downstream users,
change the function name to something hopefully clearer, and return the
adjusted rate instead of changing the pointer.
Signed-off-by: Maxime Ripard <redacted>
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
Currently, the parent rate given back to the clock framework in our
request is the original parent rate we calculated before trying to round
the rate of our clock.
This works fine unless our clock also changes its parent rate, in which
case we will simply ignore that change and still use the previous parent
rate.
Create a new function to re-adjust the parent rate to take the pre-dividers
into account, and give that back to the clock framework.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/clk/sunxi-ng/ccu_mux.c | 33 ++++++++++++++++++++++++++++-----
1 file changed, 28 insertions(+), 5 deletions(-)
@@ -64,6 +64,14 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,returnparent_rate/ccu_mux_get_prediv(common,cm,parent_index);}+unsignedlongccu_mux_helper_unapply_prediv(structccu_common*common,+structccu_mux_internal*cm,+intparent_index,+unsignedlongparent_rate)+{+returnparent_rate*ccu_mux_get_prediv(common,cm,parent_index);+}+intccu_mux_helper_determine_rate(structccu_common*common,structccu_mux_internal*cm,structclk_rate_request*req,
@@ -89,22 +97,37 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,best_rate=round(cm,best_parent,&adj_parent_rate,req->rate,data);+/*+*parent_ratemighthavebeenmodifiedbyourclock.+*Re-applythepre-dividerifthere'sone,andgive
Might want to reword the comments to match the new name.
+ * the actual frequency the parent needs to run at.
+ */
+ best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
+ adj_parent_rate);
+
goto out;
}
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- unsigned long tmp_rate, parent_rate, adj_parent_rate;
+ unsigned long tmp_rate, parent_rate;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
- parent_rate = clk_hw_get_rate(parent);
- adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
- parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
+ clk_hw_get_rate(parent));
+
+ tmp_rate = round(cm, parent, &parent_rate, req->rate, data);
- tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data);
+ /*
+ * parent_rate might have been modified by our clock.
+ * Re-apply the pre-divider if there's one, and give
Same here. Otherwise,
Acked-by: Chen-Yu Tsai <redacted>
+ * the actual frequency the parent needs to run at.
+ */
+ parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
+ parent_rate);
if (tmp_rate == req->rate) {
best_parent = parent;
best_parent_rate = parent_rate;
--
git-series 0.8.11
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
The muxing can actually happen on both channels on some SoCs, so it makes
more sense to just move it out of the sun4i_tcon1_mode_set function and
create a separate function that needs to be called by the encoders.
Let's do that and convert the existing drivers.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 +
drivers/gpu/drm/sun4i/sun4i_tcon.c | 22 ++++++++++++++++------
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 ++
drivers/gpu/drm/sun4i/sun4i_tv.c | 1 +
4 files changed, 20 insertions(+), 6 deletions(-)
The channel doesn't really matter. What is needed is which TCON and encoder
are supposed to be muxed together. This is going to be per SoC type anyway.
I have something in the works, though it's not finished yet.
I think this works for now.
Acked-by: Chen-Yu Tsai <redacted>
quoted hunk
+{
+ if (!tcon->quirks->has_unknown_mux)
+ return;
+
+ if (channel != 1)
+ return;
+
+ /*
+ * FIXME: Undocumented bits
+ */
+ regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
+}
+EXPORT_SYMBOL(sun4i_tcon_set_mux);
+
static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode,
int channel)
{
@@ -393,6 +393,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,conststructtv_mode*tv_mode=sun4i_tv_find_tv_by_mode(mode);sun4i_tcon1_mode_set(tcon,mode);+sun4i_tcon_set_mux(tcon,1,encoder);/* Enable and map the DAC to the output */regmap_update_bits(tv->regs,SUN4I_TVE_EN_REG,--
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
Even though that mux is undocumented, it seems like it needs to be set to 1
when using composite, and 0 when using HDMI.
Signed-off-by: Maxime Ripard <redacted>
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
Both TCON channels need to have the resolution doubled, since the size the
hardware is going to use is whatever we put in the register divided by two.
However, we handle it differently for the two channels: in the channel 0,
our register access macro does the multiplication of the value passed as
paremeter, while in the channel 1, the macro doesn't do this, and we need
to do it before calling it.
Make this consistent by aligning the channel 0 with the channel 1
behaviour.
Signed-off-by: Maxime Ripard <redacted>
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
It appears that the total vertical resolution needs to be doubled when
we're not in interlaced. Make sure that is the case.
I think the total vertical resolution needs to be doubled in all cases.
It just happens that you should've been using mode->crtc_vtotal, which
is halved when the mode is interlaced. Instead you used mode->vtotal,
which is double the actual scan resolution in interlaced mode.
ChenYu
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
The generic connectors such as hdmi-connector doesn't have any driver in,
so if they are added to the component list, we will be waiting forever for
a non-existing driver to probe.
Add a list of the connectors we want to ignore when building our component
list.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_drv.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
@@ -204,7 +209,8 @@ static int sun4i_drv_add_endpoints(struct device *dev,!of_device_is_available(node))return0;-if(!sun4i_drv_node_is_frontend(node)){+if(!sun4i_drv_node_is_connector(node)&&
I think it's better to just bail out (return 0) if the current node
is a connector. The connector is the end of the pipeline anyway.
Nothing after it should be considered for the component graph.
The way you currently have it, traversal will continue beyond
the connector node.
ChenYu
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
The earlier Allwinner SoCs (A10, A10s, A20, A31) have an embedded HDMI
controller.
That HDMI controller is able to do audio and CEC, but those have been left
out for now.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/Kconfig | 9 +-
drivers/gpu/drm/sun4i/Makefile | 6 +-
drivers/gpu/drm/sun4i/sun4i_hdmi.h | 157 +++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 127 +++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 493 +++++++++++++++++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 225 ++++++++++-
6 files changed, 1017 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -0,0 +1,493 @@+/*+*Copyright(C)2016MaximeRipard+*+*MaximeRipard<maxime.ripard@free-electrons.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicenseas+*publishedbytheFreeSoftwareFoundation;eitherversion2of+*theLicense,or(atyouroption)anylaterversion.+*/++#include<drm/drmP.h>+#include<drm/drm_atomic_helper.h>+#include<drm/drm_crtc_helper.h>+#include<drm/drm_edid.h>+#include<drm/drm_encoder.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>++#include<linux/clk.h>+#include<linux/component.h>+#include<linux/iopoll.h>+#include<linux/platform_device.h>+#include<linux/pm_runtime.h>++#include"sun4i_backend.h"+#include"sun4i_drv.h"+#include"sun4i_hdmi.h"+#include"sun4i_tcon.h"++#define DDC_SEGMENT_ADDR 0x30++staticinlinestructsun4i_hdmi*+drm_encoder_to_sun4i_hdmi(structdrm_encoder*encoder)+{+returncontainer_of(encoder,structsun4i_hdmi,+encoder);+}++staticinlinestructsun4i_hdmi*+drm_connector_to_sun4i_hdmi(structdrm_connector*connector)+{+returncontainer_of(connector,structsun4i_hdmi,+connector);+}++staticintsun4i_hdmi_setup_avi_infoframes(structsun4i_hdmi*hdmi,+structdrm_display_mode*mode)+{+structhdmi_avi_infoframeframe;+u8buffer[17];+inti,ret;++ret=drm_hdmi_avi_infoframe_from_display_mode(&frame,mode);+if(ret<0){+DRM_ERROR("Failed to get infoframes from mode\n");+returnret;+}++ret=hdmi_avi_infoframe_pack(&frame,buffer,sizeof(buffer));+if(ret<0){+DRM_ERROR("Failed to pack infoframes\n");+returnret;+}++for(i=0;i<sizeof(buffer);i++)+writeb(buffer[i],hdmi->base+SUN4I_HDMI_AVI_INFOFRAME_REG(i));++return0;+}++staticintsun4i_hdmi_atomic_check(structdrm_encoder*encoder,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structdrm_display_mode*mode=&crtc_state->mode;++if(mode->flags&DRM_MODE_FLAG_DBLCLK)+return-EINVAL;++return0;+}++staticvoidsun4i_hdmi_disable(structdrm_encoder*encoder)+{+structsun4i_hdmi*hdmi=drm_encoder_to_sun4i_hdmi(encoder);+structsun4i_drv*drv=hdmi->drv;+structsun4i_tcon*tcon=drv->tcon;
I assume you didn't take into consideration the latest patches I sent
that remove this field.
Instead you'll want to do drm_crtc_to_sun4i_crtc(encoder->crtc)->tcon.
/* There is no HPD interrupt, so we need to poll the controller */
hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
The A10s has an HDMI controller connected to the second TCON channel. Add
it to our DT.
Since the TV Encoder was the only channel 1 user so far, also add the
property now that we have several users.
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 50 ++++++++++++++++++++++++++++++++-
arch/arm/boot/dts/sun5i.dtsi | 1 +-
2 files changed, 51 insertions(+), 0 deletions(-)
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted hunk
The A10s Olinuxino has an HDMI connector. Make sure we can use it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 29 +++++++++++++++++-
1 file changed, 29 insertions(+), 0 deletions(-)
@@ -248,6 +273,10 @@ status = "okay"; };+&tcon0 {+ status = "okay";+};+ &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins_a>;--
git-series 0.8.11
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
It appears that the total vertical resolution needs to be doubled when
we're not in interlaced. Make sure that is the case.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c
b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 0f91ec8a4b26..efa079c1a3f5 100644
@@ -272,9 +272,9 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,/* Set vertical display timings */bp=mode->crtc_vtotal-mode->crtc_vsync_start;DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",-mode->vtotal,bp);+mode->crtc_vtotal,bp);regmap_write(tcon->regs,SUN4I_TCON1_BASIC4_REG,-SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal)|+SUN4I_TCON1_BASIC4_V_TOTAL(mode->crtc_vtotal*2)|
For TVE the value should directly be mode->vtotal, but not
mode->crtc_vtotal * 2.
vtotal is 625 when PAL. crtc_vtotal is thus 312, but if we restore the
vtotal value by doubling crtv_vtotal, we got 624, which will lead to
instability of the image displayed. (the image will loop to go higher
and then go lower, because wrong vtotal value)
Tested on patched H3 TV encoder.
I used a logic slightly changed from your v1 code:
From: Rob Herring <robh@kernel.org> Date: 2017-05-08 16:20:27
On Wed, May 03, 2017 at 01:59:53PM +0200, Maxime Ripard wrote:
One of the possible output of the display pipeline, on the SoCs that have
it, is the HDMI controller.
Add a binding for it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 79 +++++++-
1 file changed, 79 insertions(+), 0 deletions(-)
From: Rob Herring <robh@kernel.org> Date: 2017-05-08 16:24:47
On Wed, May 03, 2017 at 01:59:54PM +0200, Maxime Ripard wrote:
The Allwinner Timings Controller has two, mutually exclusive, channels.
When the binding has been introduced, it was assumed that there would be
only a single user per channel in the system.
While this is likely for the channel 0 which only connects to LCD displays,
it turns out that the channel 1 can be connected to multiple controllers in
the SoC (HDMI and TV encoders for example). And while the simultaneous use
of HDMI and TV outputs cannot be achieved, switching from one to the other
at runtime definitely sounds plausible.
Add an extra property, allwinner,tcon-channel, to specify for a given
endpoint which TCON channel it is connected to, while falling back to the
previous mechanism if that property is missing.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 11 ++++---
1 file changed, 7 insertions(+), 4 deletions(-)
From: Maxime Ripard <hidden> Date: 2017-05-10 19:24:02
Hi,
On Thu, May 04, 2017 at 04:05:18PM +0800, Chen-Yu Tsai wrote:
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted
The A10s Olinuxino has an HDMI connector. Make sure we can use it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 29 +++++++++++++++++-
1 file changed, 29 insertions(+), 0 deletions(-)
Just curious, should we take this into consideration when creating
drm_connector?
I'm not sure, no other driver does so. And I haven't seen any of our
boards with a HDMI-B, C or mini-HDMI connector.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170510/17e0817b/attachment.sig>
Hi,
On Thu, May 04, 2017 at 04:05:18PM +0800, Chen-Yu Tsai wrote:
quoted
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted
The A10s Olinuxino has an HDMI connector. Make sure we can use it.
Acked-by: Chen-Yu Tsai <redacted>
Signed-off-by: Maxime Ripard <redacted>
---
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 29 +++++++++++++++++-
1 file changed, 29 insertions(+), 0 deletions(-)
On Thu, May 4, 2017 at 3:55 PM, Chen-Yu Tsai [off-list ref] wrote:
On Wed, May 3, 2017 at 7:59 PM, Maxime Ripard
[off-list ref] wrote:
quoted
The earlier Allwinner SoCs (A10, A10s, A20, A31) have an embedded HDMI
controller.
That HDMI controller is able to do audio and CEC, but those have been left
out for now.
Signed-off-by: Maxime Ripard <redacted>
---
drivers/gpu/drm/sun4i/Kconfig | 9 +-
drivers/gpu/drm/sun4i/Makefile | 6 +-
drivers/gpu/drm/sun4i/sun4i_hdmi.h | 157 +++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 127 +++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 493 +++++++++++++++++++++-
drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 225 ++++++++++-
6 files changed, 1017 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -0,0 +1,493 @@+/*+*Copyright(C)2016MaximeRipard+*+*MaximeRipard<maxime.ripard@free-electrons.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicenseas+*publishedbytheFreeSoftwareFoundation;eitherversion2of+*theLicense,or(atyouroption)anylaterversion.+*/++#include<drm/drmP.h>+#include<drm/drm_atomic_helper.h>+#include<drm/drm_crtc_helper.h>+#include<drm/drm_edid.h>+#include<drm/drm_encoder.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>++#include<linux/clk.h>+#include<linux/component.h>+#include<linux/iopoll.h>+#include<linux/platform_device.h>+#include<linux/pm_runtime.h>++#include"sun4i_backend.h"+#include"sun4i_drv.h"+#include"sun4i_hdmi.h"+#include"sun4i_tcon.h"++#define DDC_SEGMENT_ADDR 0x30++staticinlinestructsun4i_hdmi*+drm_encoder_to_sun4i_hdmi(structdrm_encoder*encoder)+{+returncontainer_of(encoder,structsun4i_hdmi,+encoder);+}++staticinlinestructsun4i_hdmi*+drm_connector_to_sun4i_hdmi(structdrm_connector*connector)+{+returncontainer_of(connector,structsun4i_hdmi,+connector);+}++staticintsun4i_hdmi_setup_avi_infoframes(structsun4i_hdmi*hdmi,+structdrm_display_mode*mode)+{+structhdmi_avi_infoframeframe;+u8buffer[17];+inti,ret;++ret=drm_hdmi_avi_infoframe_from_display_mode(&frame,mode);+if(ret<0){+DRM_ERROR("Failed to get infoframes from mode\n");+returnret;+}++ret=hdmi_avi_infoframe_pack(&frame,buffer,sizeof(buffer));+if(ret<0){+DRM_ERROR("Failed to pack infoframes\n");+returnret;+}++for(i=0;i<sizeof(buffer);i++)+writeb(buffer[i],hdmi->base+SUN4I_HDMI_AVI_INFOFRAME_REG(i));++return0;+}++staticintsun4i_hdmi_atomic_check(structdrm_encoder*encoder,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structdrm_display_mode*mode=&crtc_state->mode;++if(mode->flags&DRM_MODE_FLAG_DBLCLK)+return-EINVAL;++return0;+}++staticvoidsun4i_hdmi_disable(structdrm_encoder*encoder)+{+structsun4i_hdmi*hdmi=drm_encoder_to_sun4i_hdmi(encoder);+structsun4i_drv*drv=hdmi->drv;+structsun4i_tcon*tcon=drv->tcon;
I assume you didn't take into consideration the latest patches I sent
that remove this field.
Instead you'll want to do drm_crtc_to_sun4i_crtc(encoder->crtc)->tcon.
/* There is no HPD interrupt, so we need to poll the controller */
hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;