Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1309387 > unrolled thread
| Started by | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| First post | 2016-01-14 16:30 +0100 |
| Last post | 2016-01-14 16:50 +0100 |
| Articles | 20 on this page of 36 — 4 participants |
Back to article view | Back to linux.kernel
[PATCH v2 00/26] drm: Add Allwinner A10 display engine support Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 03/26] clk: Add regmap support Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 22/26] ARM: sun5i: r8: Add display blocks to the DTSI Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 20/26] drm: sun4i: tv: Add PAL output standard Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 21/26] drm: sun4i: tv: Add NTSC output standard Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 10/26] ARM: sun5i: a13: Add display and TCON clocks Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 18/26] drm: sun4i: Add RGB output Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 23/26] ARM: sun5i: chip: Enable the TV Encoder Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 11/26] ARM: sun5i: Add DRAM gates Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 19/26] drm: sun4i: Add composite output Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 08/26] clk: sun5i: add DRAM gates Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
Re: [PATCH v2 08/26] clk: sun5i: add DRAM gates Rob Herring <robh@kernel.org> - 2016-01-15 04:10 +0100
[PATCH v2 06/26] clk: sunxi: Add PLL3 clock Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
Re: [PATCH v2 06/26] clk: sunxi: Add PLL3 clock Rob Herring <robh@kernel.org> - 2016-01-15 04:10 +0100
[PATCH v2 04/26] clk: composite: Add unregister function Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:30 +0100
[PATCH v2 02/26] reset: Make reset_control_ops const Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 07/26] clk: sunxi: Add TCON channel1 clock Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 07/26] clk: sunxi: Add TCON channel1 clock Rob Herring <robh@kernel.org> - 2016-01-15 04:10 +0100
[PATCH v2 26/26] DO NOT MERGE: ARM: sun5i: chip: Enable the LCD panel Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 15/26] drm/modes: Support modes names on the command line Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 14/26] drm/modes: Rewrite the command line parser Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 17/26] drm: sun4i: Add DT bindings documentation Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 17/26] drm: sun4i: Add DT bindings documentation Rob Herring <robh@kernel.org> - 2016-01-15 04:20 +0100
[PATCH v2 12/26] ARM: sun5i: Add TV encoder gate to the DTSI Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 25/26] drm/panel: simple: Add timings for the Olimex LCD-OLinuXino-4.3TS Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 25/26] drm/panel: simple: Add timings for the Olimex LCD-OLinuXino-4.3TS Rob Herring <robh@kernel.org> - 2016-01-15 04:20 +0100
[PATCH v2 13/26] drm/fb_cma_helper: Remove implicit call to disable_unused_functions Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 13/26] drm/fb_cma_helper: Remove implicit call to disable_unused_functions Laurent Pinchart <laurent.pinchart@ideasonboard.com> - 2016-01-15 00:20 +0100
[PATCH v2 05/26] clk: sunxi: Add display and TCON0 clocks driver Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 05/26] clk: sunxi: Add display and TCON0 clocks driver Rob Herring <robh@kernel.org> - 2016-01-15 04:10 +0100
[PATCH v2 24/26] devicetree: Add olimex vendor prefix Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
Re: [PATCH v2 24/26] devicetree: Add olimex vendor prefix Rob Herring <robh@kernel.org> - 2016-01-15 04:20 +0100
Re: [PATCH v2 24/26] devicetree: Add olimex vendor prefix Stefan Wahren <stefan.wahren@i2se.com> - 2016-01-15 07:50 +0100
Re: [PATCH v2 24/26] devicetree: Add olimex vendor prefix Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-15 09:10 +0100
[PATCH v2 09/26] ARM: sun5i: dt: Add pll3 and pll7 clocks Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:40 +0100
[PATCH v2 01/26] reset: Move DT cell size check to the core Maxime Ripard <maxime.ripard@free-electrons.com> - 2016-01-14 16:50 +0100
Page 1 of 2 [1] 2 Next page →
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 00/26] drm: Add Allwinner A10 display engine support |
| Message-ID | <qQS0x-uX-7@gated-at.bofh.it> |
Hi everyone,
The Allwinner SoCs (except for the very latest ones) all share the
same set of controllers, loosely coupled together to form the display
pipeline.
Depending on the SoC, the number of instances of the controller will
change (2 instances of each in the A10, only one in the A13, for
example), and the output availables will change too (HDMI, composite,
VGA on the A20, none of them on the A13).
On most featured SoCs, it looks like that:
+--------------------------------------------+
| RAM |
+--------------------------------------------+
| | | |
v | | v
+----------------+ | | +----------------+
| Frontend | | | | Frontend |
+----------------+ | | +----------------+
| | | |
v | | v
+----------------+ | | +----------------+
| Backend |<+ +>| Backend |
+----------------+ +----------------+
| |
v v
+----------------+ +----------------+---> LVDS
| TCON | | TCON |---> RGB
+----------------+ +----------------+
| +---+ +---+ |
| | | |
v v v v
+------------+ +------------+ +------------+---> VGA
| TV Encoder | | HDMI | | TV Encoder |---> Composite
+------------+ +------------+ +------------+
The current code only assumes that there is a single instance of all
the controllers. It also supports only the RGB and Composite
interfaces.
Let me know what you think,
Maxime
Changes from v1:
- Rebased on top of 4.4
- Merged the clock drivers for the display and TCON channel 0 clocks
- Replaced the container_of calls in the display reset clocks to an
inline function
- Checked the return code of of_clk_parent_fill in the clocks
drivers
- Checked the return code of of_clk_add_provider in the tcon-ch1 and
PLL3 clocks
- Added missing clocks headers
- Created a composite clock unregister function
- Moved the binding documentation to
Documentation/devicetree/bindings/display
- Added the clocks binding documentation
- Added the Olimex vendor to the list of DT vendors
- Moved to the OF graph representation and the component framework
- Moved the reset cells count check into the reset framework to
avoid duplicating the code in every xlate implementation.
- Made the reset_ops const
- Reworked the DRM cmdline mode parsing code to allow named mode
- Fixed the TV mode lookup when the mode name is not present (for
example because it was given by the userspace)
- Made the driver outputs optional (to avoid crashing when a board
doesn't have either a panel or a composite output enabled)
- Added multiple plane support with transparency
- Moved the backend registers writes commit in the CRTC atomic_flush
callback
- Removed the load / unload functions
- Removed the enabled booleans in my private structure and removed
the implicit call to disable_unused_functions in the DRM core to
push it in the drivers.
- Fixed a few bitmasks on some bitfields definition
- Fixed the RGB connector mode validation that was not testing the
right values
Matthias Brugger (1):
clk: Add regmap support
Maxime Ripard (25):
reset: Move DT cell size check to the core
reset: Make reset_control_ops const
clk: composite: Add unregister function
clk: sunxi: Add display and TCON0 clocks driver
clk: sunxi: Add PLL3 clock
clk: sunxi: Add TCON channel1 clock
clk: sun5i: add DRAM gates
ARM: sun5i: dt: Add pll3 and pll7 clocks
ARM: sun5i: a13: Add display and TCON clocks
ARM: sun5i: Add DRAM gates
ARM: sun5i: Add TV encoder gate to the DTSI
drm/fb_cma_helper: Remove implicit call to disable_unused_functions
drm/modes: Rewrite the command line parser
drm/modes: Support modes names on the command line
drm: Add Allwinner A10 Display Engine support
drm: sun4i: Add DT bindings documentation
drm: sun4i: Add RGB output
drm: sun4i: Add composite output
drm: sun4i: tv: Add PAL output standard
drm: sun4i: tv: Add NTSC output standard
ARM: sun5i: r8: Add display blocks to the DTSI
ARM: sun5i: chip: Enable the TV Encoder
devicetree: Add olimex vendor prefix
drm/panel: simple: Add timings for the Olimex LCD-OLinuXino-4.3TS
DO NOT MERGE: ARM: sun5i: chip: Enable the LCD panel
Documentation/devicetree/bindings/clock/sunxi.txt | 5 +
.../display/panel/olimex,lcd-olinuxino-43-ts.txt | 7 +
.../bindings/display/sunxi/sun4i-drm.txt | 228 +++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/arm/boot/dts/sun5i-a10s.dtsi | 7 +-
arch/arm/boot/dts/sun5i-a13.dtsi | 46 +-
arch/arm/boot/dts/sun5i-r8-chip.dts | 17 +
arch/arm/boot/dts/sun5i-r8.dtsi | 139 ++++-
arch/arm/boot/dts/sun5i.dtsi | 62 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-composite.c | 15 +
drivers/clk/clk-divider.c | 68 ++-
drivers/clk/clk-gate.c | 54 +-
drivers/clk/clk-io.c | 48 ++
drivers/clk/clk-io.h | 22 +
drivers/clk/clk-mux.c | 94 ++-
drivers/clk/sunxi/Makefile | 3 +
drivers/clk/sunxi/clk-simple-gates.c | 2 +
drivers/clk/sunxi/clk-sun4i-display.c | 241 ++++++++
drivers/clk/sunxi/clk-sun4i-pll3.c | 90 +++
drivers/clk/sunxi/clk-sun4i-tcon-ch1.c | 154 +++++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_crtc.c | 3 +-
drivers/gpu/drm/drm_fb_cma_helper.c | 3 -
drivers/gpu/drm/drm_fb_helper.c | 4 +
drivers/gpu/drm/drm_modes.c | 327 ++++++----
drivers/gpu/drm/imx/imx-drm-core.c | 1 +
drivers/gpu/drm/panel/panel-simple.c | 26 +
drivers/gpu/drm/sti/sti_drv.c | 1 +
drivers/gpu/drm/sun4i/Kconfig | 14 +
drivers/gpu/drm/sun4i/Makefile | 12 +
drivers/gpu/drm/sun4i/sun4i_backend.c | 321 ++++++++++
drivers/gpu/drm/sun4i/sun4i_backend.h | 160 +++++
drivers/gpu/drm/sun4i/sun4i_crtc.c | 120 ++++
drivers/gpu/drm/sun4i/sun4i_crtc.h | 30 +
drivers/gpu/drm/sun4i/sun4i_drv.c | 325 ++++++++++
drivers/gpu/drm/sun4i/sun4i_drv.h | 30 +
drivers/gpu/drm/sun4i/sun4i_framebuffer.c | 54 ++
drivers/gpu/drm/sun4i/sun4i_framebuffer.h | 19 +
drivers/gpu/drm/sun4i/sun4i_layer.c | 160 +++++
drivers/gpu/drm/sun4i/sun4i_layer.h | 30 +
drivers/gpu/drm/sun4i/sun4i_rgb.c | 229 +++++++
drivers/gpu/drm/sun4i/sun4i_rgb.h | 18 +
drivers/gpu/drm/sun4i/sun4i_tcon.c | 521 ++++++++++++++++
drivers/gpu/drm/sun4i/sun4i_tcon.h | 177 ++++++
drivers/gpu/drm/sun4i/sun4i_tv.c | 675 +++++++++++++++++++++
drivers/gpu/drm/tilcdc/tilcdc_drv.c | 1 +
drivers/reset/core.c | 6 +-
include/drm/drm_modes.h | 1 +
include/linux/clk-provider.h | 55 +-
include/linux/reset-controller.h | 2 +-
52 files changed, 4455 insertions(+), 179 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino-43-ts.txt
create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
create mode 100644 drivers/clk/clk-io.c
create mode 100644 drivers/clk/clk-io.h
create mode 100644 drivers/clk/sunxi/clk-sun4i-display.c
create mode 100644 drivers/clk/sunxi/clk-sun4i-pll3.c
create mode 100644 drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
create mode 100644 drivers/gpu/drm/sun4i/Kconfig
create mode 100644 drivers/gpu/drm/sun4i/Makefile
create mode 100644 drivers/gpu/drm/sun4i/sun4i_backend.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_backend.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_crtc.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_crtc.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_drv.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_drv.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_framebuffer.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_framebuffer.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_layer.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_layer.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_rgb.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_rgb.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_tcon.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_tcon.h
create mode 100644 drivers/gpu/drm/sun4i/sun4i_tv.c
--
2.6.4
[toc] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 03/26] clk: Add regmap support |
| Message-ID | <qQS0x-uX-13@gated-at.bofh.it> |
| In reply to | #1309387 |
From: Matthias Brugger <matthias.bgg@gmail.com>
Some devices like SoCs from Mediatek need to use the clock
through a regmap interface.
This patch adds regmap support for the simple multiplexer clock,
the divider clock and the clock gate code.
Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/clk/Makefile | 1 +
drivers/clk/clk-divider.c | 68 +++++++++++++++++++++++++-------
drivers/clk/clk-gate.c | 54 +++++++++++++++++++------
drivers/clk/clk-io.c | 48 ++++++++++++++++++++++
drivers/clk/clk-io.h | 22 +++++++++++
drivers/clk/clk-mux.c | 94 +++++++++++++++++++++++++++++++++++++-------
include/linux/clk-provider.h | 54 +++++++++++++++++++++++--
7 files changed, 298 insertions(+), 43 deletions(-)
create mode 100644 drivers/clk/clk-io.c
create mode 100644 drivers/clk/clk-io.h
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 820714c72d36..31a888c0c182 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
obj-$(CONFIG_COMMON_CLK) += clk-gpio.o
+obj-$(CONFIG_COMMON_CLK) += clk-io.o
ifeq ($(CONFIG_OF), y)
obj-$(CONFIG_COMMON_CLK) += clk-conf.o
endif
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 3ace102a2a0a..1ddb353b3fff 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -18,6 +18,8 @@
#include <linux/string.h>
#include <linux/log2.h>
+#include "clk-io.h"
+
/*
* DOC: basic adjustable divider clock that cannot gate
*
@@ -142,7 +144,8 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
struct clk_divider *divider = to_clk_divider(hw);
unsigned int val;
- val = clk_readl(divider->reg) >> divider->shift;
+ val = clk_io_readl(hw, divider->reg, divider->regmap, divider->offset);
+ val >>= divider->shift;
val &= div_mask(divider->width);
return divider_recalc_rate(hw, parent_rate, val, divider->table,
@@ -354,7 +357,10 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- bestdiv = readl(divider->reg) >> divider->shift;
+ bestdiv = clk_io_readl(hw, divider->reg, divider->regmap,
+ divider->offset);
+
+ bestdiv >>= divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
@@ -400,12 +406,16 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = div_mask(divider->width) << (divider->shift + 16);
+ val |= value << divider->shift;
+ clk_io_writel(hw, divider->reg, divider->regmap,
+ divider->offset, val);
} else {
- val = clk_readl(divider->reg);
- val &= ~(div_mask(divider->width) << divider->shift);
+ u32 mask = div_mask(divider->width) << divider->shift;
+
+ val = value << divider->shift;
+ clk_io_update_bits(hw, divider->reg, divider->regmap,
+ divider->offset, mask, val);
}
- val |= value << divider->shift;
- clk_writel(val, divider->reg);
if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags);
@@ -424,9 +434,9 @@ EXPORT_SYMBOL_GPL(clk_divider_ops);
static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
- spinlock_t *lock)
+ void __iomem *reg, struct regmap *regmap, u32 offset,
+ u8 shift, u8 width, u8 clk_divider_flags,
+ const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_divider *div;
struct clk *clk;
@@ -451,7 +461,12 @@ static struct clk *_register_divider(struct device *dev, const char *name,
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_divider assignments */
- div->reg = reg;
+ if (flags & CLK_USE_REGMAP)
+ div->regmap = regmap;
+ else
+ div->reg = reg;
+
+ div->offset = offset;
div->shift = shift;
div->width = width;
div->flags = clk_divider_flags;
@@ -485,8 +500,8 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, spinlock_t *lock)
{
- return _register_divider(dev, name, parent_name, flags, reg, shift,
- width, clk_divider_flags, NULL, lock);
+ return _register_divider(dev, name, parent_name, flags, reg, NULL, 0,
+ shift, width, clk_divider_flags, NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_register_divider);
@@ -510,8 +525,8 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
u8 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock)
{
- return _register_divider(dev, name, parent_name, flags, reg, shift,
- width, clk_divider_flags, table, lock);
+ return _register_divider(dev, name, parent_name, flags, reg, NULL, 0,
+ shift, width, clk_divider_flags, table, lock);
}
EXPORT_SYMBOL_GPL(clk_register_divider_table);
@@ -530,3 +545,28 @@ void clk_unregister_divider(struct clk *clk)
kfree(div);
}
EXPORT_SYMBOL_GPL(clk_unregister_divider);
+
+struct clk *clk_regm_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ flags |= CLK_USE_REGMAP;
+
+ return _register_divider(dev, name, parent_name, flags, NULL, regmap,
+ offset, shift, width, clk_divider_flags, NULL, lock);
+}
+EXPORT_SYMBOL_GPL(clk_regm_register_divider);
+
+struct clk *clk_regm_register_divider_table(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock)
+{
+ flags |= CLK_USE_REGMAP;
+
+ return _register_divider(dev, name, parent_name, flags, NULL, regmap,
+ offset, shift, width, clk_divider_flags, table, lock);
+}
+EXPORT_SYMBOL_GPL(clk_regm_register_divider_table);
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index de0b322f5f58..38b558397631 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -16,6 +16,8 @@
#include <linux/err.h>
#include <linux/string.h>
+#include "clk-io.h"
+
/**
* DOC: basic gatable clock which can gate and ungate it's ouput
*
@@ -46,7 +48,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
struct clk_gate *gate = to_clk_gate(hw);
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
unsigned long uninitialized_var(flags);
- u32 reg;
+ u32 reg, mask;
set ^= enable;
@@ -59,16 +61,20 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
reg = BIT(gate->bit_idx + 16);
if (set)
reg |= BIT(gate->bit_idx);
- } else {
- reg = clk_readl(gate->reg);
+ clk_io_writel(hw, gate->reg, gate->regmap, gate->offset, reg);
+ } else {
if (set)
- reg |= BIT(gate->bit_idx);
+ reg = BIT(gate->bit_idx);
else
- reg &= ~BIT(gate->bit_idx);
+ reg = 0x0;
+
+ mask = BIT(gate->bit_idx);
+
+ clk_io_update_bits(hw, gate->reg, gate->regmap, gate->offset,
+ mask, reg);
}
- clk_writel(reg, gate->reg);
if (gate->lock)
spin_unlock_irqrestore(gate->lock, flags);
@@ -93,7 +99,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw)
u32 reg;
struct clk_gate *gate = to_clk_gate(hw);
- reg = clk_readl(gate->reg);
+ reg = clk_io_readl(hw, gate->reg, gate->regmap, gate->offset);
/* if a set bit disables this clk, flip it before masking */
if (gate->flags & CLK_GATE_SET_TO_DISABLE)
@@ -122,10 +128,10 @@ EXPORT_SYMBOL_GPL(clk_gate_ops);
* @clk_gate_flags: gate-specific flags for this clock
* @lock: shared register lock for this clock
*/
-struct clk *clk_register_gate(struct device *dev, const char *name,
+struct clk *__clk_register_gate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 bit_idx,
- u8 clk_gate_flags, spinlock_t *lock)
+ void __iomem *reg, struct regmap *regmap, u32 offset,
+ u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock)
{
struct clk_gate *gate;
struct clk *clk;
@@ -150,7 +156,12 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_gate assignments */
- gate->reg = reg;
+ if (flags & CLK_USE_REGMAP)
+ gate->regmap = regmap;
+ else
+ gate->reg = reg;
+
+ gate->offset = offset;
gate->bit_idx = bit_idx;
gate->flags = clk_gate_flags;
gate->lock = lock;
@@ -163,6 +174,15 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
return clk;
}
+
+struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
+{
+ return __clk_register_gate(dev, name, parent_name, flags,
+ reg, NULL, 0, bit_idx, clk_gate_flags, lock);
+}
EXPORT_SYMBOL_GPL(clk_register_gate);
void clk_unregister_gate(struct clk *clk)
@@ -180,3 +200,15 @@ void clk_unregister_gate(struct clk *clk)
kfree(gate);
}
EXPORT_SYMBOL_GPL(clk_unregister_gate);
+
+struct clk *clk_regm_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
+{
+ flags |= CLK_USE_REGMAP;
+
+ return __clk_register_gate(dev, name, parent_name, flags,
+ NULL, regmap, offset, bit_idx, clk_gate_flags, lock);
+}
+EXPORT_SYMBOL_GPL(clk_regm_register_gate);
diff --git a/drivers/clk/clk-io.c b/drivers/clk/clk-io.c
new file mode 100644
index 000000000000..ceeb64e15ddb
--- /dev/null
+++ b/drivers/clk/clk-io.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 Matthias Brugger <matthias.bgg@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk-provider.h>
+
+void clk_io_writel(struct clk_hw *hw, void __iomem *reg, struct regmap *regmap,
+ u32 offset, u32 val)
+{
+ if (__clk_get_flags(hw->clk) & CLK_USE_REGMAP)
+ regmap_write(regmap, offset, val);
+ else
+ clk_writel(val, reg);
+}
+
+u32 clk_io_readl(struct clk_hw *hw, void __iomem *reg, struct regmap *regmap,
+ u32 offset)
+{
+ u32 val;
+
+ if (__clk_get_flags(hw->clk) & CLK_USE_REGMAP)
+ regmap_read(regmap, offset, &val);
+ else
+ val = clk_readl(reg);
+
+ return val;
+}
+
+int clk_io_update_bits(struct clk_hw *hw, void __iomem *reg,
+ struct regmap *regmap, u32 offset, u32 mask, u32 val)
+{
+ unsigned int tmp;
+
+ if (__clk_get_flags(hw->clk) & CLK_USE_REGMAP)
+ return regmap_update_bits(regmap, offset, mask, val);
+
+ tmp = clk_readl(reg);
+ tmp &= ~mask;
+ tmp |= val;
+ clk_writel(tmp, reg);
+
+ return 0;
+}
diff --git a/drivers/clk/clk-io.h b/drivers/clk/clk-io.h
new file mode 100644
index 000000000000..b5599ba02084
--- /dev/null
+++ b/drivers/clk/clk-io.h
@@ -0,0 +1,22 @@
+/*
+ * linux/drivers/clk/clk-io.h
+ *
+ * Copyright (C) 2015 Matthias Brugger <matthias.bgg@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_CLK_IO_H
+#define __LINUX_CLK_IO_H
+
+#include <linux/clk-provider.h>
+
+void clk_io_writel(struct clk_hw *hw, void __iomem *reg, struct regmap *regmap,
+ u32 offset, u32 val);
+u32 clk_io_readl(struct clk_hw *hw, void __iomem *reg, struct regmap *regmap,
+ u32 offset);
+int clk_io_update_bits(struct clk_hw *hw, void __iomem *reg,
+ struct regmap *regmap, u32 offset, u32 mask, u32 val);
+
+#endif
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 7129c86a79db..d3831eca8b56 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -16,6 +16,8 @@
#include <linux/io.h>
#include <linux/err.h>
+#include "clk-io.h"
+
/*
* DOC: basic adjustable multiplexer clock that cannot gate
*
@@ -41,7 +43,9 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
* OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
* val = 0x4 really means "bit 2, index starts at bit 0"
*/
- val = clk_readl(mux->reg) >> mux->shift;
+ val = clk_io_readl(hw, mux->reg, mux->regmap, mux->offset);
+
+ val >>= mux->shift;
val &= mux->mask;
if (mux->table) {
@@ -70,6 +74,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
struct clk_mux *mux = to_clk_mux(hw);
u32 val;
unsigned long flags = 0;
+ int ret = 0;
if (mux->table)
index = mux->table[index];
@@ -89,19 +94,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
if (mux->flags & CLK_MUX_HIWORD_MASK) {
val = mux->mask << (mux->shift + 16);
+ val |= index << mux->shift;
+ clk_io_writel(hw, mux->reg, mux->regmap, mux->offset, val);
} else {
- val = clk_readl(mux->reg);
- val &= ~(mux->mask << mux->shift);
+ u32 mask = mux->mask << mux->shift;
+
+ val = index << mux->shift;
+ ret = clk_io_update_bits(hw, mux->reg, mux->regmap,
+ mux->offset, mask, val);
}
- val |= index << mux->shift;
- clk_writel(val, mux->reg);
if (mux->lock)
spin_unlock_irqrestore(mux->lock, flags);
else
__release(mux->lock);
- return 0;
+ return ret;
}
const struct clk_ops clk_mux_ops = {
@@ -116,10 +124,11 @@ const struct clk_ops clk_mux_ro_ops = {
};
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
-struct clk *clk_register_mux_table(struct device *dev, const char *name,
+struct clk *__clk_register_mux_table(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
- void __iomem *reg, u8 shift, u32 mask,
+ void __iomem *reg, struct regmap *regmap,
+ u32 offset, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{
struct clk_mux *mux;
@@ -152,7 +161,12 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
init.num_parents = num_parents;
/* struct clk_mux assignments */
- mux->reg = reg;
+ if (flags & CLK_USE_REGMAP)
+ mux->regmap = regmap;
+ else
+ mux->reg = reg;
+
+ mux->offset = offset;
mux->shift = shift;
mux->mask = mask;
mux->flags = clk_mux_flags;
@@ -167,19 +181,40 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
return clk;
}
+
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+{
+ return __clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, NULL, 0,
+ shift, mask, clk_mux_flags,
+ table, lock);
+}
EXPORT_SYMBOL_GPL(clk_register_mux_table);
-struct clk *clk_register_mux(struct device *dev, const char *name,
+struct clk *__clk_register_mux(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
- unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
+ unsigned long flags, void __iomem *reg, struct regmap *regmap,
+ u32 offset, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
{
u32 mask = BIT(width) - 1;
- return clk_register_mux_table(dev, name, parent_names, num_parents,
- flags, reg, shift, mask, clk_mux_flags,
- NULL, lock);
+ return __clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, regmap, offset, shift, mask,
+ clk_mux_flags, NULL, lock);
+}
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
+{
+ return __clk_register_mux(dev, name, parent_names, num_parents, flags,
+ reg, NULL, 0, shift, width,
+ clk_mux_flags, lock);
}
EXPORT_SYMBOL_GPL(clk_register_mux);
@@ -198,3 +233,32 @@ void clk_unregister_mux(struct clk *clk)
kfree(mux);
}
EXPORT_SYMBOL_GPL(clk_unregister_mux);
+
+struct clk *clk_regm_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, struct regmap *regmap,
+ u32 offset, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
+{
+ flags |= CLK_USE_REGMAP;
+
+ return __clk_register_mux(dev, name, parent_names, num_parents, flags,
+ NULL, regmap, offset, shift, width,
+ clk_mux_flags, lock);
+}
+EXPORT_SYMBOL_GPL(clk_regm_register_mux);
+
+struct clk *clk_regm_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, struct regmap *regmap,
+ u32 offset, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+{
+ flags |= CLK_USE_REGMAP;
+
+ return __clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, NULL, regmap, offset,
+ shift, mask, clk_mux_flags,
+ table, lock);
+}
+EXPORT_SYMBOL_GPL(clk_regm_register_mux_table);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index c56988ac63f7..0c55a37cf8f7 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/regmap.h>
#ifdef CONFIG_COMMON_CLK
@@ -31,6 +32,7 @@
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */
+#define CLK_USE_REGMAP BIT(10) /* uses regmap to access registers */
struct clk;
struct clk_hw;
@@ -290,6 +292,8 @@ void of_fixed_clk_setup(struct device_node *np);
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register controlling gate
+ * @regmap: regmap used to control the gate
+ * @offset: offset inside the regmap
* @bit_idx: single bit controlling gate
* @flags: hardware-specific flags
* @lock: register lock
@@ -307,7 +311,11 @@ void of_fixed_clk_setup(struct device_node *np);
*/
struct clk_gate {
struct clk_hw hw;
- void __iomem *reg;
+ union {
+ void __iomem *reg;
+ struct regmap *regmap;
+ };
+ u32 offset;
u8 bit_idx;
u8 flags;
spinlock_t *lock;
@@ -323,6 +331,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
u8 clk_gate_flags, spinlock_t *lock);
void clk_unregister_gate(struct clk *clk);
+struct clk *clk_regm_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock);
+
struct clk_div_table {
unsigned int val;
unsigned int div;
@@ -333,6 +346,8 @@ struct clk_div_table {
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register containing the divider
+ * @regmap: regmap used to access the divider
+ * @offest: offset inside the regmap
* @shift: shift to the divider bit field
* @width: width of the divider bit field
* @table: array of value/divider pairs, last entry should have div = 0
@@ -367,7 +382,11 @@ struct clk_div_table {
*/
struct clk_divider {
struct clk_hw hw;
- void __iomem *reg;
+ union {
+ void __iomem *reg;
+ struct regmap *regmap;
+ };
+ u32 offset;
u8 shift;
u8 width;
u8 flags;
@@ -406,11 +425,24 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
spinlock_t *lock);
void clk_unregister_divider(struct clk *clk);
+struct clk *clk_regm_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock);
+
+struct clk *clk_regm_register_divider_table(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags,
+ struct regmap *regmap, u32 offset, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock);
+
/**
* struct clk_mux - multiplexer clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register controlling multiplexer
+ * @regmap: regmap for controlling multiplexer
+ * @offset: offset inside the regmap
* @shift: shift to multiplexer bit field
* @width: width of mutliplexer bit field
* @flags: hardware-specific flags
@@ -431,8 +463,12 @@ void clk_unregister_divider(struct clk *clk);
*/
struct clk_mux {
struct clk_hw hw;
- void __iomem *reg;
+ union {
+ void __iomem *reg;
+ struct regmap *regmap;
+ };
u32 *table;
+ u32 offset;
u32 mask;
u8 shift;
u8 flags;
@@ -462,6 +498,18 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
void clk_unregister_mux(struct clk *clk);
+struct clk *clk_regm_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, struct regmap *regmap,
+ u32 offset, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+
+struct clk *clk_regm_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, struct regmap *regmap,
+ u32 offset, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock);
+
void of_fixed_factor_clk_setup(struct device_node *node);
/**
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 22/26] ARM: sun5i: r8: Add display blocks to the DTSI |
| Message-ID | <qQS0y-uX-15@gated-at.bofh.it> |
| In reply to | #1309387 |
The TCON, tv-encoder and display engine backends and frontends are combined
to create our display pipeline.
Add them to the R8 DTSI. It's supposed to be perfectly compatible with the
A10s and A13, but since we haven't tested it on them yet, it's safer to
just enable it on the R8. Eventually, it should be moved to sun5i.dtsi
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun5i-r8.dtsi | 134 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 134 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-r8.dtsi b/arch/arm/boot/dts/sun5i-r8.dtsi
index 691d3de75b35..b3bf91516a80 100644
--- a/arch/arm/boot/dts/sun5i-r8.dtsi
+++ b/arch/arm/boot/dts/sun5i-r8.dtsi
@@ -57,4 +57,138 @@
status = "disabled";
};
};
+
+ soc@01c00000 {
+ tve0: tv-encoder@01c0a000 {
+ compatible = "allwinner,sun4i-a10-tv-encoder";
+ reg = <0x01c0a000 0x1000>;
+ clocks = <&ahb_gates 34>;
+ status = "disabled";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tve0_in_tcon0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_out_tve0>;
+ };
+ };
+ };
+
+ tcon0: lcd-controller@01c0c000 {
+ compatible = "allwinner,sun4i-a10-tcon";
+ reg = <0x01c0c000 0x1000>;
+ interrupts = <44>;
+ clocks = <&ahb_gates 36>,
+ <&tcon_ch0_clk>,
+ <&tcon_ch1_clk>;
+ clock-names = "ahb",
+ "tcon-ch0",
+ "tcon-ch1";
+ clock-output-names = "tcon-pixel-clock";
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tcon0_in: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ tcon0_in_be0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&be0_out_tcon0>;
+ };
+ };
+
+ tcon0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ tcon0_out_tve0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tve0_in_tcon0>;
+ };
+ };
+ };
+ };
+
+ fe0: display-frontend@01e00000 {
+ compatible = "allwinner,sun5i-a13-display-frontend";
+ reg = <0x01e00000 0x20000>;
+ interrupts = <47>;
+ clocks = <&ahb_gates 46>, <&de_fe_clk>,
+ <&dram_gates 25>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&de_fe_clk>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fe0_out: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ fe0_out_be0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&be0_in_fe0>;
+ };
+ };
+ };
+ };
+
+ be0: display-backend@01e60000 {
+ compatible = "allwinner,sun5i-a13-display-backend";
+ reg = <0x01e60000 0x10000>;
+ clocks = <&ahb_gates 44>, <&de_be_clk>,
+ <&dram_gates 26>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&de_be_clk>;
+ status = "disabled";
+
+ assigned-clocks = <&de_be_clk>;
+ assigned-clock-rates = <300000000>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ be0_in: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ be0_in_fe0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&fe0_out_be0>;
+ };
+ };
+
+ be0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ be0_out_tcon0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_in_be0>;
+ };
+ };
+ };
+ };
+ };
+
+ display-engine {
+ compatible = "allwinner,sun5i-a13-display-engine";
+ pipelines = <&be0>;
+ };
};
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 20/26] drm: sun4i: tv: Add PAL output standard |
| Message-ID | <qQS0x-uX-5@gated-at.bofh.it> |
| In reply to | #1309387 |
Now that we have support for the composite output, we can start adding new
supported standards. Start with PAL, and we will add other eventually.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/gpu/drm/sun4i/sun4i_tv.c | 42 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 4f10f8b6b07c..4f369de2a1fc 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -176,7 +176,49 @@ struct sun4i_tv {
struct sun4i_drv *drv;
};
+struct video_levels pal_video_levels = {
+ .black = 252, .blank = 252,
+};
+
+struct burst_levels pal_burst_levels = {
+ .cb = 40, .cr = 40,
+};
+
+struct color_gains pal_color_gains = {
+ .cb = 224, .cr = 224,
+};
+
+struct resync_parameters pal_resync_parameters = {
+ .field = true, .line = 13, .pixel = 12,
+};
+
struct tv_mode tv_modes[] = {
+ {
+ .name = "PAL",
+ .mode = SUN4I_TVE_CFG0_RES_576i,
+ .chroma_freq = 0x2a098acb,
+
+ .back_porch = 138,
+ .front_porch = 24,
+ .line_number = 625,
+
+ .hdisplay = 720,
+ .hfront_porch = 3,
+ .hsync_len = 2,
+ .hback_porch = 139,
+
+ .vdisplay = 576,
+ .vfront_porch = 28,
+ .vsync_len = 2,
+ .vback_porch = 19,
+
+ .vblank_level = 252,
+
+ .color_gains = &pal_color_gains,
+ .burst_levels = &pal_burst_levels,
+ .video_levels = &pal_video_levels,
+ .resync_params = &pal_resync_parameters,
+ },
};
static inline struct sun4i_tv *
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 21/26] drm: sun4i: tv: Add NTSC output standard |
| Message-ID | <qQS0y-uX-17@gated-at.bofh.it> |
| In reply to | #1309387 |
Add the settings to support the NTSC standard.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/gpu/drm/sun4i/sun4i_tv.c | 45 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 4f369de2a1fc..46331087b0e9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -176,24 +176,69 @@ struct sun4i_tv {
struct sun4i_drv *drv;
};
+struct video_levels ntsc_video_levels = {
+ .black = 282, .blank = 240,
+};
+
struct video_levels pal_video_levels = {
.black = 252, .blank = 252,
};
+struct burst_levels ntsc_burst_levels = {
+ .cb = 79, .cr = 0,
+};
+
struct burst_levels pal_burst_levels = {
.cb = 40, .cr = 40,
};
+struct color_gains ntsc_color_gains = {
+ .cb = 160, .cr = 160,
+};
+
struct color_gains pal_color_gains = {
.cb = 224, .cr = 224,
};
+struct resync_parameters ntsc_resync_parameters = {
+ .field = false, .line = 14, .pixel = 12,
+};
+
struct resync_parameters pal_resync_parameters = {
.field = true, .line = 13, .pixel = 12,
};
struct tv_mode tv_modes[] = {
{
+ .name = "NTSC",
+ .mode = SUN4I_TVE_CFG0_RES_480i,
+ .chroma_freq = 0x21f07c1f,
+ .yc_en = true,
+ .dac3_en = true,
+ .dac_bit25_en = true,
+
+ .back_porch = 118,
+ .front_porch = 32,
+ .line_number = 525,
+
+ .hdisplay = 720,
+ .hfront_porch = 18,
+ .hsync_len = 2,
+ .hback_porch = 118,
+
+ .vdisplay = 480,
+ .vfront_porch = 26,
+ .vsync_len = 2,
+ .vback_porch = 17,
+
+ .vblank_level = 240,
+
+ .color_gains = &ntsc_color_gains,
+ .burst_levels = &ntsc_burst_levels,
+ .video_levels = &ntsc_video_levels,
+ .resync_params = &ntsc_resync_parameters,
+ },
+ {
.name = "PAL",
.mode = SUN4I_TVE_CFG0_RES_576i,
.chroma_freq = 0x2a098acb,
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 10/26] ARM: sun5i: a13: Add display and TCON clocks |
| Message-ID | <qQS0y-uX-21@gated-at.bofh.it> |
| In reply to | #1309387 |
Enable the display and TCON (channel 0 and channel 1) clocks that are going
to be needed to drive the display engine, tcon and TV encoders.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun5i-a13.dtsi | 38 +++++++++++++++++++++++++++++++++++++-
arch/arm/boot/dts/sun5i-r8.dtsi | 5 +++--
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index d910d3a6c41c..9669b03f20f3 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -61,7 +61,8 @@
compatible = "allwinner,simple-framebuffer",
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0";
- clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>;
+ clocks = <&ahb_gates 36>, <&ahb_gates 44>, <&de_be_clk>,
+ <&tcon_ch0_clk>;
status = "disabled";
};
};
@@ -149,6 +150,41 @@
"apb1_i2c2", "apb1_uart1",
"apb1_uart3";
};
+
+ de_be_clk: clk@01c20104 {
+ #clock-cells = <0>;
+ #reset-cells = <0>;
+ compatible = "allwinner,sun4i-a10-display-clk";
+ reg = <0x01c20104 0x4>;
+ clocks = <&pll3>, <&pll7>, <&pll5 1>;
+ clock-output-names = "de-be";
+ };
+
+ de_fe_clk: clk@01c2010c {
+ #clock-cells = <0>;
+ #reset-cells = <0>;
+ compatible = "allwinner,sun4i-a10-display-clk";
+ reg = <0x01c2010c 0x4>;
+ clocks = <&pll3>, <&pll7>, <&pll5 1>;
+ clock-output-names = "de-fe";
+ };
+
+ tcon_ch0_clk: clk@01c20118 {
+ #clock-cells = <0>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun4i-a10-tcon-ch0-clk";
+ reg = <0x01c20118 0x4>;
+ clocks = <&pll3>, <&pll7>, <&pll3x2>, <&pll7x2>;
+ clock-output-names = "tcon-ch0-sclk";
+ };
+
+ tcon_ch1_clk: clk@01c2012c {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-tcon-ch1-clk";
+ reg = <0x01c2012c 0x4>;
+ clocks = <&pll3>, <&pll7>, <&pll3x2>, <&pll7x2>;
+ clock-output-names = "tcon-ch1-sclk";
+ };
};
soc@01c00000 {
diff --git a/arch/arm/boot/dts/sun5i-r8.dtsi b/arch/arm/boot/dts/sun5i-r8.dtsi
index 0ef865601ac9..b1e4e0170d51 100644
--- a/arch/arm/boot/dts/sun5i-r8.dtsi
+++ b/arch/arm/boot/dts/sun5i-r8.dtsi
@@ -51,8 +51,9 @@
compatible = "allwinner,simple-framebuffer",
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0-tve0";
- clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
- <&ahb_gates 44>;
+ clocks = <&ahb_gates 34>, <&ahb_gates 36>,
+ <&ahb_gates 44>, <&de_be_clk>,
+ <&tcon_ch1_clk>;
status = "disabled";
};
};
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 18/26] drm: sun4i: Add RGB output |
| Message-ID | <qQS0y-uX-33@gated-at.bofh.it> |
| In reply to | #1309387 |
One of the A10 display pipeline possible output is an RGB interface to
drive LCD panels directly. This is done through the first channel of the
TCON that will output our video signals directly.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/gpu/drm/sun4i/Makefile | 2 +
drivers/gpu/drm/sun4i/sun4i_rgb.c | 229 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/sun4i/sun4i_rgb.h | 18 +++
drivers/gpu/drm/sun4i/sun4i_tcon.c | 26 ++++-
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 +
5 files changed, 276 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_rgb.c
create mode 100644 drivers/gpu/drm/sun4i/sun4i_rgb.h
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 7b0e83bae867..a7c9e7fd5a26 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -3,6 +3,8 @@ sun4i-drm-y += sun4i_drv.o
sun4i-drm-y += sun4i_framebuffer.o
sun4i-drm-y += sun4i_layer.o
+sun4i-drm-y += sun4i_rgb.o
+
obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
new file mode 100644
index 000000000000..c2b366e39749
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
+
+struct sun4i_rgb {
+ struct drm_connector connector;
+ struct drm_encoder encoder;
+
+ struct sun4i_drv *drv;
+};
+
+static inline struct sun4i_rgb *
+drm_connector_to_sun4i_rgb(struct drm_connector *connector)
+{
+ return container_of(connector, struct sun4i_rgb,
+ connector);
+}
+
+static inline struct sun4i_rgb *
+drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct sun4i_rgb,
+ encoder);
+}
+
+static int sun4i_rgb_get_modes(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb =
+ drm_connector_to_sun4i_rgb(connector);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ return drm_panel_get_modes(tcon->panel);
+}
+
+static int sun4i_rgb_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ u32 hsync = mode->hsync_end - mode->hsync_start;
+ u32 vsync = mode->vsync_end - mode->vsync_start;
+
+ DRM_DEBUG_DRIVER("Validating modes...\n");
+
+ if ((hsync < 1) || (hsync > 0x3ff) ||
+ (mode->htotal < 1) || (mode->htotal > 0xfff))
+ return MODE_H_ILLEGAL;
+
+ DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
+
+ if ((vsync < 1) || (vsync > 0x3ff) ||
+ (mode->vtotal < 1) || (mode->vtotal > 0xfff))
+ return MODE_V_ILLEGAL;
+
+ DRM_DEBUG_DRIVER("Vertical parameters OK\n");
+
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+sun4i_rgb_best_encoder(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb =
+ drm_connector_to_sun4i_rgb(connector);
+
+ return &rgb->encoder;
+}
+
+static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
+ .get_modes = sun4i_rgb_get_modes,
+ .mode_valid = sun4i_rgb_mode_valid,
+ .best_encoder = sun4i_rgb_best_encoder,
+};
+
+static enum drm_connector_status
+sun4i_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void
+sun4i_rgb_connector_destroy(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ drm_panel_detach(tcon->panel);
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs sun4i_rgb_con_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = sun4i_rgb_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = sun4i_rgb_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Enabling RGB output\n");
+
+ drm_panel_enable(tcon->panel);
+ sun4i_tcon_channel_enable(tcon, 0);
+}
+
+static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Disabling RGB output\n");
+
+ sun4i_tcon_channel_disable(tcon, 0);
+ drm_panel_disable(tcon->panel);
+}
+
+static bool sun4i_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted)
+{
+ return true;
+}
+
+static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ sun4i_tcon0_mode_set(tcon, mode);
+
+ clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
+}
+
+static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
+ .mode_fixup = sun4i_rgb_encoder_mode_fixup,
+ .mode_set = sun4i_rgb_encoder_mode_set,
+ .disable = sun4i_rgb_encoder_disable,
+ .enable = sun4i_rgb_encoder_enable,
+};
+
+static void sun4i_rgb_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
+ .destroy = sun4i_rgb_enc_destroy,
+};
+
+int sun4i_rgb_init(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon = drv->tcon;
+ struct sun4i_rgb *rgb;
+ int ret;
+
+ /* If we don't have a panel, there's no point in going on */
+ if (!tcon->panel)
+ return -ENODEV;
+
+ rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
+ if (!rgb)
+ return -ENOMEM;
+ rgb->drv = drv;
+
+ drm_encoder_helper_add(&rgb->encoder,
+ &sun4i_rgb_enc_helper_funcs);
+ ret = drm_encoder_init(drm,
+ &rgb->encoder,
+ &sun4i_rgb_enc_funcs,
+ DRM_MODE_ENCODER_NONE);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialise the rgb encoder\n");
+ goto err_out;
+ }
+
+ /* The RGB encoder can only work with the TCON channel 0 */
+ rgb->encoder.possible_crtcs = BIT(0);
+
+ drm_connector_helper_add(&rgb->connector,
+ &sun4i_rgb_con_helper_funcs);
+ ret = drm_connector_init(drm, &rgb->connector,
+ &sun4i_rgb_con_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
+ goto err_cleanup_connector;
+ }
+
+ drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+
+ drm_panel_attach(tcon->panel, &rgb->connector);
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(&rgb->encoder);
+err_out:
+ return ret;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.h b/drivers/gpu/drm/sun4i/sun4i_rgb.h
new file mode 100644
index 000000000000..7c4da4c8acdd
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN4I_RGB_H_
+#define _SUN4I_RGB_H_
+
+int sun4i_rgb_init(struct drm_device *drm);
+
+#endif /* _SUN4I_RGB_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index e253818b711b..6a6dc47a3b42 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -15,6 +15,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
#include <linux/clk-provider.h>
#include <linux/component.h>
@@ -25,6 +26,7 @@
#include "sun4i_crtc.h"
#include "sun4i_drv.h"
+#include "sun4i_rgb.h"
#include "sun4i_tcon.h"
void sun4i_tcon_disable(struct sun4i_tcon *tcon)
@@ -418,6 +420,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
struct sun4i_tcon *tcon;
+ struct device_node *np;
int ret;
tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
@@ -444,7 +447,20 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
goto err_free_clocks;
}
- return 0;
+ np = of_parse_phandle(dev->of_node, "allwinner,panel", 0);
+ if (!np) {
+ dev_info(dev, "No panel found... RGB output disabled\n");
+ return 0;
+ }
+
+ tcon->panel = of_drm_find_panel(np);
+ if (!tcon->panel) {
+ dev_err(dev, "Couldn't find our panel\n");
+ ret = -ENODEV;
+ goto err_free_clocks;
+ }
+
+ return sun4i_rgb_init(drm);
err_free_clocks:
sun4i_tcon_free_clocks(tcon);
@@ -466,6 +482,14 @@ static struct component_ops sun4i_tcon_ops = {
static int sun4i_tcon_probe(struct platform_device *pdev)
{
+ struct device_node *np;
+
+ np = of_parse_phandle(pdev->dev.of_node, "allwinner,panel", 0);
+ if (np) {
+ if (!of_drm_find_panel(np))
+ return -EPROBE_DEFER;
+ }
+
return component_add(&pdev->dev, &sun4i_tcon_ops);
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 6fa093bce974..9496912b6484 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -152,6 +152,8 @@ struct sun4i_tcon {
/* Pixel clock */
struct clk *dclk;
+
+ struct drm_panel *panel;
};
/* Global Control */
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 23/26] ARM: sun5i: chip: Enable the TV Encoder |
| Message-ID | <qQS0y-uX-37@gated-at.bofh.it> |
| In reply to | #1309387 |
The CHIP has a composite output available muxed with the microphone in the
micro-jack plug.
Enable the composite output in its DTS.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun5i-r8-chip.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-r8-chip.dts b/arch/arm/boot/dts/sun5i-r8-chip.dts
index c976c7618118..c26c095b42c6 100644
--- a/arch/arm/boot/dts/sun5i-r8-chip.dts
+++ b/arch/arm/boot/dts/sun5i-r8-chip.dts
@@ -84,6 +84,10 @@
};
};
+&be0 {
+ status = "okay";
+};
+
&codec {
status = "okay";
};
@@ -230,6 +234,14 @@
status = "okay";
};
+&tcon0 {
+ status = "okay";
+};
+
+&tve0 {
+ status = "okay";
+};
+
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&uart1_pins_b>;
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 11/26] ARM: sun5i: Add DRAM gates |
| Message-ID | <qQS0y-uX-41@gated-at.bofh.it> |
| In reply to | #1309387 |
The DRAM gates control whether the image / display devices on the SoC have
access to the DRAM clock or not.
Enable it.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 7 ++++---
arch/arm/boot/dts/sun5i-a13.dtsi | 2 +-
arch/arm/boot/dts/sun5i-r8.dtsi | 2 +-
arch/arm/boot/dts/sun5i.dtsi | 19 +++++++++++++++++++
4 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index bddd0de88af6..52d2c79cb37b 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -66,7 +66,7 @@
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0-hdmi";
clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
- <&ahb_gates 44>;
+ <&ahb_gates 44>, <&dram_gates 26>;
status = "disabled";
};
@@ -74,7 +74,8 @@
compatible = "allwinner,simple-framebuffer",
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0";
- clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>;
+ clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>,
+ <&dram_gates 26>;
status = "disabled";
};
@@ -83,7 +84,7 @@
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0-tve0";
clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
- <&ahb_gates 44>;
+ <&ahb_gates 44>, <&dram_gates 26>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 9669b03f20f3..f29163650ca8 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -62,7 +62,7 @@
"simple-framebuffer";
allwinner,pipeline = "de_be0-lcd0";
clocks = <&ahb_gates 36>, <&ahb_gates 44>, <&de_be_clk>,
- <&tcon_ch0_clk>;
+ <&tcon_ch0_clk>, <&dram_gates 26>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/sun5i-r8.dtsi b/arch/arm/boot/dts/sun5i-r8.dtsi
index b1e4e0170d51..691d3de75b35 100644
--- a/arch/arm/boot/dts/sun5i-r8.dtsi
+++ b/arch/arm/boot/dts/sun5i-r8.dtsi
@@ -53,7 +53,7 @@
allwinner,pipeline = "de_be0-lcd0-tve0";
clocks = <&ahb_gates 34>, <&ahb_gates 36>,
<&ahb_gates 44>, <&de_be_clk>,
- <&tcon_ch1_clk>;
+ <&tcon_ch1_clk>, <&dram_gates 26>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi
index 0840612b5ed6..c72d94228915 100644
--- a/arch/arm/boot/dts/sun5i.dtsi
+++ b/arch/arm/boot/dts/sun5i.dtsi
@@ -338,6 +338,25 @@
clock-output-names = "usb_ohci0", "usb_phy";
};
+ dram_gates: clk@01c20100 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun5i-a13-dram-gates-clk";
+ reg = <0x01c20100 0x4>;
+ clocks = <&pll5 0>;
+ clock-indices = <0>,
+ <1>,
+ <25>,
+ <26>,
+ <29>,
+ <31>;
+ clock-output-names = "dram_ve",
+ "dram_csi",
+ "dram_de_fe",
+ "dram_de_be",
+ "dram_ace",
+ "dram_iep";
+ };
+
codec_clk: clk@01c20140 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-codec-clk";
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 19/26] drm: sun4i: Add composite output |
| Message-ID | <qQS0y-uX-35@gated-at.bofh.it> |
| In reply to | #1309387 |
Some Allwinner SoCs have an IP called the TV encoder that is used to output
composite and VGA signals. In such a case, we need to use the second TCON
channel.
Add support for that TV encoder.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/gpu/drm/sun4i/Makefile | 1 +
drivers/gpu/drm/sun4i/sun4i_tv.c | 588 +++++++++++++++++++++++++++++++++++++++
2 files changed, 589 insertions(+)
create mode 100644 drivers/gpu/drm/sun4i/sun4i_tv.c
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index a7c9e7fd5a26..c458b09181fe 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_tcon.o
+obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
new file mode 100644
index 000000000000..4f10f8b6b07c
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "sun4i_backend.h"
+#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
+
+#define SUN4I_TVE_EN_REG 0x000
+#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4)
+#define SUN4I_TVE_EN_DAC_MAP(dac, out) (((out) & 0xf) << (dac + 1) * 4)
+#define SUN4I_TVE_EN_ENABLE BIT(0)
+
+#define SUN4I_TVE_CFG0_REG 0x004
+#define SUN4I_TVE_CFG0_DAC_CONTROL_54M BIT(26)
+#define SUN4I_TVE_CFG0_CORE_DATAPATH_54M BIT(25)
+#define SUN4I_TVE_CFG0_CORE_CONTROL_54M BIT(24)
+#define SUN4I_TVE_CFG0_YC_EN BIT(17)
+#define SUN4I_TVE_CFG0_COMP_EN BIT(16)
+#define SUN4I_TVE_CFG0_RES(x) ((x) & 0xf)
+#define SUN4I_TVE_CFG0_RES_480i SUN4I_TVE_CFG0_RES(0)
+#define SUN4I_TVE_CFG0_RES_576i SUN4I_TVE_CFG0_RES(1)
+
+#define SUN4I_TVE_DAC0_REG 0x008
+#define SUN4I_TVE_DAC0_CLOCK_INVERT BIT(24)
+#define SUN4I_TVE_DAC0_LUMA(x) (((x) & 3) << 20)
+#define SUN4I_TVE_DAC0_LUMA_0_4 SUN4I_TVE_DAC0_LUMA(3)
+#define SUN4I_TVE_DAC0_CHROMA(x) (((x) & 3) << 18)
+#define SUN4I_TVE_DAC0_CHROMA_0_75 SUN4I_TVE_DAC0_CHROMA(3)
+#define SUN4I_TVE_DAC0_INTERNAL_DAC(x) (((x) & 3) << 16)
+#define SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS SUN4I_TVE_DAC0_INTERNAL_DAC(3)
+#define SUN4I_TVE_DAC0_DAC_EN(dac) BIT(dac)
+
+#define SUN4I_TVE_NOTCH_REG 0x00c
+#define SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(dac, x) ((4 - (x)) << (dac * 3))
+
+#define SUN4I_TVE_CHROMA_FREQ_REG 0x010
+
+#define SUN4I_TVE_PORCH_REG 0x014
+#define SUN4I_TVE_PORCH_BACK(x) ((x) << 16)
+#define SUN4I_TVE_PORCH_FRONT(x) (x)
+
+#define SUN4I_TVE_LINE_REG 0x01c
+#define SUN4I_TVE_LINE_FIRST(x) ((x) << 16)
+#define SUN4I_TVE_LINE_NUMBER(x) (x)
+
+#define SUN4I_TVE_LEVEL_REG 0x020
+#define SUN4I_TVE_LEVEL_BLANK(x) ((x) << 16)
+#define SUN4I_TVE_LEVEL_BLACK(x) (x)
+
+#define SUN4I_TVE_DAC1_REG 0x024
+#define SUN4I_TVE_DAC1_AMPLITUDE(dac, x) ((x) << (dac * 8))
+
+#define SUN4I_TVE_DETECT_STA_REG 0x038
+#define SUN4I_TVE_DETECT_STA_DAC(dac) BIT((dac * 8))
+#define SUN4I_TVE_DETECT_STA_UNCONNECTED 0
+#define SUN4I_TVE_DETECT_STA_CONNECTED 1
+#define SUN4I_TVE_DETECT_STA_GROUND 2
+
+#define SUN4I_TVE_CB_CR_LVL_REG 0x10c
+#define SUN4I_TVE_CB_CR_LVL_CR_BURST(x) ((x) << 8)
+#define SUN4I_TVE_CB_CR_LVL_CB_BURST(x) (x)
+
+#define SUN4I_TVE_TINT_BURST_PHASE_REG 0x110
+#define SUN4I_TVE_TINT_BURST_PHASE_CHROMA(x) (x)
+
+#define SUN4I_TVE_BURST_WIDTH_REG 0x114
+#define SUN4I_TVE_BURST_WIDTH_BREEZEWAY(x) ((x) << 16)
+#define SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(x) ((x) << 8)
+#define SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(x) (x)
+
+#define SUN4I_TVE_CB_CR_GAIN_REG 0x118
+#define SUN4I_TVE_CB_CR_GAIN_CR(x) ((x) << 8)
+#define SUN4I_TVE_CB_CR_GAIN_CB(x) (x)
+
+#define SUN4I_TVE_SYNC_VBI_REG 0x11c
+#define SUN4I_TVE_SYNC_VBI_SYNC(x) ((x) << 16)
+#define SUN4I_TVE_SYNC_VBI_VBLANK(x) (x)
+
+#define SUN4I_TVE_ACTIVE_LINE_REG 0x124
+#define SUN4I_TVE_ACTIVE_LINE(x) (x)
+
+#define SUN4I_TVE_CHROMA_REG 0x128
+#define SUN4I_TVE_CHROMA_COMP_GAIN(x) ((x) & 3)
+#define SUN4I_TVE_CHROMA_COMP_GAIN_50 SUN4I_TVE_CHROMA_COMP_GAIN(2)
+
+#define SUN4I_TVE_12C_REG 0x12c
+#define SUN4I_TVE_12C_NOTCH_WIDTH_WIDE BIT(8)
+#define SUN4I_TVE_12C_COMP_YUV_EN BIT(0)
+
+#define SUN4I_TVE_RESYNC_REG 0x130
+#define SUN4I_TVE_RESYNC_FIELD BIT(31)
+#define SUN4I_TVE_RESYNC_LINE(x) ((x) << 16)
+#define SUN4I_TVE_RESYNC_PIXEL(x) (x)
+
+#define SUN4I_TVE_SLAVE_REG 0x134
+
+#define SUN4I_TVE_WSS_DATA2_REG 0x244
+
+struct color_gains {
+ u16 cb;
+ u16 cr;
+};
+
+struct burst_levels {
+ u16 cb;
+ u16 cr;
+};
+
+struct video_levels {
+ u16 black;
+ u16 blank;
+};
+
+struct resync_parameters {
+ bool field;
+ u16 line;
+ u16 pixel;
+};
+
+struct tv_mode {
+ char *name;
+
+ u32 mode;
+ u32 chroma_freq;
+ u16 back_porch;
+ u16 front_porch;
+ u16 line_number;
+ u16 vblank_level;
+
+ u32 hdisplay;
+ u16 hfront_porch;
+ u16 hsync_len;
+ u16 hback_porch;
+
+ u32 vdisplay;
+ u16 vfront_porch;
+ u16 vsync_len;
+ u16 vback_porch;
+
+ bool yc_en;
+ bool dac3_en;
+ bool dac_bit25_en;
+
+ struct color_gains *color_gains;
+ struct burst_levels *burst_levels;
+ struct video_levels *video_levels;
+ struct resync_parameters *resync_params;
+};
+
+struct sun4i_tv {
+ struct drm_connector connector;
+ struct drm_encoder encoder;
+
+ struct clk *clk;
+ struct regmap *regs;
+
+ struct sun4i_drv *drv;
+};
+
+struct tv_mode tv_modes[] = {
+};
+
+static inline struct sun4i_tv *
+drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct sun4i_tv,
+ encoder);
+}
+
+static inline struct sun4i_tv *
+drm_connector_to_sun4i_tv(struct drm_connector *connector)
+{
+ return container_of(connector, struct sun4i_tv,
+ connector);
+}
+
+/*
+ * FIXME: If only the drm_display_mode private field was usable, this
+ * could go away...
+ *
+ * So far, it doesn't seem to be preserved when the mode is passed by
+ * to mode_set for some reason.
+ */
+static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
+{
+ int i;
+
+ /* First try to identify the mode by name */
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
+ mode->name, tv_mode->name);
+
+ if (!strcmp(mode->name, tv_mode->name))
+ return tv_mode;
+ }
+
+ /* Then by number of lines */
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
+ mode->name, tv_mode->name,
+ mode->vdisplay, tv_mode->vdisplay);
+
+ if (mode->vdisplay == tv_mode->vdisplay)
+ return tv_mode;
+ }
+
+ return NULL;
+}
+
+static int sun4i_tv_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return 0;
+}
+
+static void sun4i_tv_disable(struct drm_encoder *encoder)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Disabling the TV Output\n");
+
+ sun4i_tcon_channel_disable(tcon, 1);
+
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_ENABLE,
+ 0);
+}
+
+static void sun4i_tv_enable(struct drm_encoder *encoder)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Enabling the TV Output\n");
+
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_ENABLE,
+ SUN4I_TVE_EN_ENABLE);
+
+ sun4i_tcon_channel_enable(tcon, 1);
+}
+
+static void sun4i_tv_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+ struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
+
+ sun4i_backend_apply_color_correction(drv->backend);
+ sun4i_tcon1_mode_set(tcon, mode);
+
+ /* Enable and map the DAC to the output */
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_DAC_MAP_MASK,
+ SUN4I_TVE_EN_DAC_MAP(0, 1) |
+ SUN4I_TVE_EN_DAC_MAP(1, 2) |
+ SUN4I_TVE_EN_DAC_MAP(2, 3) |
+ SUN4I_TVE_EN_DAC_MAP(3, 4));
+
+ /* Set PAL settings */
+ regmap_write(tv->regs, SUN4I_TVE_CFG0_REG,
+ tv_mode->mode |
+ (tv_mode->yc_en ? SUN4I_TVE_CFG0_YC_EN : 0) |
+ SUN4I_TVE_CFG0_COMP_EN |
+ SUN4I_TVE_CFG0_DAC_CONTROL_54M |
+ SUN4I_TVE_CFG0_CORE_DATAPATH_54M |
+ SUN4I_TVE_CFG0_CORE_CONTROL_54M);
+
+ /* Configure the DAC for a composite output */
+ regmap_write(tv->regs, SUN4I_TVE_DAC0_REG,
+ SUN4I_TVE_DAC0_DAC_EN(0) |
+ (tv_mode->dac3_en ? SUN4I_TVE_DAC0_DAC_EN(3) : 0) |
+ SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS |
+ SUN4I_TVE_DAC0_CHROMA_0_75 |
+ SUN4I_TVE_DAC0_LUMA_0_4 |
+ SUN4I_TVE_DAC0_CLOCK_INVERT |
+ (tv_mode->dac_bit25_en ? BIT(25) : 0) |
+ BIT(30));
+
+ /* Configure the sample delay between DAC0 and the other DAC */
+ regmap_write(tv->regs, SUN4I_TVE_NOTCH_REG,
+ SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(1, 0) |
+ SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(2, 0));
+
+ regmap_write(tv->regs, SUN4I_TVE_CHROMA_FREQ_REG,
+ tv_mode->chroma_freq);
+
+ /* Set the front and back porch */
+ regmap_write(tv->regs, SUN4I_TVE_PORCH_REG,
+ SUN4I_TVE_PORCH_BACK(tv_mode->back_porch) |
+ SUN4I_TVE_PORCH_FRONT(tv_mode->front_porch));
+
+ /* Set the lines setup */
+ regmap_write(tv->regs, SUN4I_TVE_LINE_REG,
+ SUN4I_TVE_LINE_FIRST(22) |
+ SUN4I_TVE_LINE_NUMBER(tv_mode->line_number));
+
+ regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG,
+ SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) |
+ SUN4I_TVE_LEVEL_BLACK(tv_mode->video_levels->black));
+
+ regmap_write(tv->regs, SUN4I_TVE_DAC1_REG,
+ SUN4I_TVE_DAC1_AMPLITUDE(0, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(1, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(2, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(3, 0x18));
+
+ regmap_write(tv->regs, SUN4I_TVE_CB_CR_LVL_REG,
+ SUN4I_TVE_CB_CR_LVL_CB_BURST(tv_mode->burst_levels->cb) |
+ SUN4I_TVE_CB_CR_LVL_CR_BURST(tv_mode->burst_levels->cr));
+
+ /* Set burst width for a composite output */
+ regmap_write(tv->regs, SUN4I_TVE_BURST_WIDTH_REG,
+ SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(126) |
+ SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(68) |
+ SUN4I_TVE_BURST_WIDTH_BREEZEWAY(22));
+
+ regmap_write(tv->regs, SUN4I_TVE_CB_CR_GAIN_REG,
+ SUN4I_TVE_CB_CR_GAIN_CB(tv_mode->color_gains->cb) |
+ SUN4I_TVE_CB_CR_GAIN_CR(tv_mode->color_gains->cr));
+
+ regmap_write(tv->regs, SUN4I_TVE_SYNC_VBI_REG,
+ SUN4I_TVE_SYNC_VBI_SYNC(0x10) |
+ SUN4I_TVE_SYNC_VBI_VBLANK(tv_mode->vblank_level));
+
+ regmap_write(tv->regs, SUN4I_TVE_ACTIVE_LINE_REG,
+ SUN4I_TVE_ACTIVE_LINE(1440));
+
+ /* Set composite chroma gain to 50 % */
+ regmap_write(tv->regs, SUN4I_TVE_CHROMA_REG,
+ SUN4I_TVE_CHROMA_COMP_GAIN_50);
+
+ regmap_write(tv->regs, SUN4I_TVE_12C_REG,
+ SUN4I_TVE_12C_COMP_YUV_EN |
+ SUN4I_TVE_12C_NOTCH_WIDTH_WIDE);
+
+ regmap_write(tv->regs, SUN4I_TVE_RESYNC_REG,
+ SUN4I_TVE_RESYNC_PIXEL(tv_mode->resync_params->pixel) |
+ SUN4I_TVE_RESYNC_LINE(tv_mode->resync_params->line) |
+ (tv_mode->resync_params->field ?
+ SUN4I_TVE_RESYNC_FIELD : 0));
+
+ regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0);
+
+ clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
+}
+
+static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
+ .atomic_check = sun4i_tv_atomic_check,
+ .disable = sun4i_tv_disable,
+ .enable = sun4i_tv_enable,
+ .mode_set = sun4i_tv_mode_set,
+};
+
+static void sun4i_tv_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs sun4i_tv_funcs = {
+ .destroy = sun4i_tv_destroy,
+};
+
+static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ DRM_DEBUG_DRIVER("Creating mode %s\n", tv_mode->name);
+
+ strcpy(mode->name, tv_mode->name);
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ mode->clock = 13500;
+ mode->flags = DRM_MODE_FLAG_INTERLACE;
+
+ mode->hdisplay = tv_mode->hdisplay;
+ mode->hsync_start = mode->hdisplay + tv_mode->hfront_porch;
+ mode->hsync_end = mode->hsync_start + tv_mode->hsync_len;
+ mode->htotal = mode->hsync_end + tv_mode->hback_porch;
+
+ mode->vdisplay = tv_mode->vdisplay;
+ mode->vsync_start = mode->vdisplay + tv_mode->vfront_porch;
+ mode->vsync_end = mode->vsync_start + tv_mode->vsync_len;
+ mode->vtotal = mode->vsync_end + tv_mode->vback_porch;
+
+ drm_mode_probed_add(connector, mode);
+ }
+
+ return i;
+}
+
+static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /* TODO */
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+sun4i_tv_comp_best_encoder(struct drm_connector *connector)
+{
+ struct sun4i_tv *tv = drm_connector_to_sun4i_tv(connector);
+
+ return &tv->encoder;
+}
+
+static struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
+ .get_modes = sun4i_tv_comp_get_modes,
+ .mode_valid = sun4i_tv_comp_mode_valid,
+ .best_encoder = sun4i_tv_comp_best_encoder,
+};
+
+static enum drm_connector_status
+sun4i_tv_comp_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void
+sun4i_tv_comp_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs sun4i_tv_comp_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = sun4i_tv_comp_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = sun4i_tv_comp_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct regmap_config sun4i_tv_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SUN4I_TVE_WSS_DATA2_REG,
+ .name = "tv-encoder",
+};
+
+static int sun4i_tv_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tv *tv;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ tv = devm_kzalloc(dev, sizeof(*tv), GFP_KERNEL);
+ if (!tv)
+ return -ENOMEM;
+ tv->drv = drv;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(dev, "Couldn't map the TV encoder registers\n");
+ return PTR_ERR(regs);
+ }
+
+ tv->regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_tv_regmap_config);
+ if (IS_ERR(tv->regs)) {
+ dev_err(dev, "Couldn't create the TV encoder regmap\n");
+ return PTR_ERR(tv->regs);
+ }
+
+ tv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(tv->clk)) {
+ dev_err(dev, "Couldn't get the TV encoder clock\n");
+ return PTR_ERR(tv->clk);
+ }
+ clk_prepare_enable(tv->clk);
+
+ drm_encoder_helper_add(&tv->encoder,
+ &sun4i_tv_helper_funcs);
+ ret = drm_encoder_init(drm,
+ &tv->encoder,
+ &sun4i_tv_funcs,
+ DRM_MODE_ENCODER_TVDAC);
+ if (ret) {
+ dev_err(dev, "Couldn't initialise the TV encoder\n");
+ return ret;
+ }
+
+ tv->encoder.possible_crtcs = BIT(0);
+
+ drm_connector_helper_add(&tv->connector,
+ &sun4i_tv_comp_connector_helper_funcs);
+ ret = drm_connector_init(drm, &tv->connector,
+ &sun4i_tv_comp_connector_funcs,
+ DRM_MODE_CONNECTOR_Composite);
+ if (ret) {
+ dev_err(dev,
+ "Couldn't initialise the Composite connector\n");
+ goto err_cleanup_connector;
+ }
+ tv->connector.interlace_allowed = true;
+
+ drm_mode_connector_attach_encoder(&tv->connector, &tv->encoder);
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(&tv->encoder);
+ return ret;
+}
+
+static void sun4i_tv_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+#warning FIXME
+}
+
+static struct component_ops sun4i_tv_ops = {
+ .bind = sun4i_tv_bind,
+ .unbind = sun4i_tv_unbind,
+};
+
+static int sun4i_tv_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &sun4i_tv_ops);
+}
+
+static int sun4i_tv_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sun4i_tv_ops);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_tv_of_table[] = {
+ { .compatible = "allwinner,sun4i-a10-tv-encoder" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_tv_of_table);
+
+static struct platform_driver sun4i_tv_platform_driver = {
+ .probe = sun4i_tv_probe,
+ .remove = sun4i_tv_remove,
+ .driver = {
+ .name = "sun4i-tve",
+ .of_match_table = sun4i_tv_of_table,
+ },
+};
+module_platform_driver(sun4i_tv_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 TV Encoder Driver");
+MODULE_LICENSE("GPL");
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 08/26] clk: sun5i: add DRAM gates |
| Message-ID | <qQS0z-uX-43@gated-at.bofh.it> |
| In reply to | #1309387 |
The Allwinner SoCs have a gate controller to gate the access to the DRAM clock to the some devices that need to access the DRAM directly (mostly display / image related IPs). Use a simple gates driver to support the one found in the A13 / R8 SoCs. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Chen-Yu Tsai <wens@csie.org> --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-simple-gates.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index fe34fc56e803..2a76cc3718d4 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -57,6 +57,7 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun4i-a10-display-clk" - for the display clocks on the A10 + "allwinner,sun5i-a13-dram-gates-clk" - for the DRAM gates on A13 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "allwinner,sun4i-a10-mmc-clk" - for the MMC clock "allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80 diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index 0214c6548afd..5666c767fa14 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -112,6 +112,8 @@ CLK_OF_DECLARE(sun5i_a13_apb0, "allwinner,sun5i-a13-apb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun5i_a13_apb1, "allwinner,sun5i-a13-apb1-gates-clk", sunxi_simple_gates_init); +CLK_OF_DECLARE(sun5i_a13_dram, "allwinner,sun5i-a13-dram-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun6i_a31_apb1, "allwinner,sun6i-a31-apb1-gates-clk", -- 2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-01-15 04:10 +0100 |
| Subject | Re: [PATCH v2 08/26] clk: sun5i: add DRAM gates |
| Message-ID | <qR2VY-8p8-7@gated-at.bofh.it> |
| In reply to | #1309402 |
On Thu, Jan 14, 2016 at 04:24:51PM +0100, Maxime Ripard wrote: > The Allwinner SoCs have a gate controller to gate the access to the DRAM > clock to the some devices that need to access the DRAM directly (mostly > display / image related IPs). > > Use a simple gates driver to support the one found in the A13 / R8 SoCs. > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Chen-Yu Tsai <wens@csie.org> > --- > Documentation/devicetree/bindings/clock/sunxi.txt | 1 + Acked-by: Rob Herring <robh@kernel.org> > drivers/clk/sunxi/clk-simple-gates.c | 2 ++ > 2 files changed, 3 insertions(+)
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 06/26] clk: sunxi: Add PLL3 clock |
| Message-ID | <qQS0z-uX-45@gated-at.bofh.it> |
| In reply to | #1309387 |
The A10 SoCs and relatives have a PLL controller to drive the PLL3 and
PLL7, clocked from a 3MHz oscillator, that drives the display related
clocks (GPU, display engine, TCON, etc.)
Add a driver for it.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
Documentation/devicetree/bindings/clock/sunxi.txt | 1 +
drivers/clk/sunxi/Makefile | 1 +
drivers/clk/sunxi/clk-sun4i-pll3.c | 90 +++++++++++++++++++++++
3 files changed, 92 insertions(+)
create mode 100644 drivers/clk/sunxi/clk-sun4i-pll3.c
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index 5360554a7d3f..bb9fb78dcff8 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -10,6 +10,7 @@ Required properties:
"allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
"allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
+ "allwinner,sun4i-a10-pll3-clk" - for the video PLL clock on A10
"allwinner,sun9i-a80-pll4-clk" - for the peripheral PLLs on A80
"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index a991cd8ca509..40c32ffd912c 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -11,6 +11,7 @@ obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o
obj-y += clk-simple-gates.o
obj-y += clk-sun4i-display.o
+obj-y += clk-sun4i-pll3.o
obj-y += clk-sun8i-mbus.o
obj-y += clk-sun9i-core.o
obj-y += clk-sun9i-mmc.o
diff --git a/drivers/clk/sunxi/clk-sun4i-pll3.c b/drivers/clk/sunxi/clk-sun4i-pll3.c
new file mode 100644
index 000000000000..6c9c2210b6b2
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun4i-pll3.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define SUN4I_A10_PLL3_GATE_BIT 31
+#define SUN4I_A10_PLL3_DIV_WIDTH 7
+#define SUN4I_A10_PLL3_DIV_SHIFT 0
+
+static DEFINE_SPINLOCK(sun4i_a10_pll3_lock);
+
+static void __init sun4i_a10_pll3_setup(struct device_node *node)
+{
+ const char *clk_name = node->name, *parent;
+ struct clk_multiplier *mult;
+ struct clk_gate *gate;
+ void __iomem *reg;
+ struct clk *clk;
+ int ret;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ parent = of_clk_get_parent_name(node, 0);
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n", clk_name);
+ return;
+ }
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return;
+
+ gate->reg = reg;
+ gate->bit_idx = SUN4I_A10_PLL3_GATE_BIT;
+ gate->lock = &sun4i_a10_pll3_lock;
+
+ mult = kzalloc(sizeof(*mult), GFP_KERNEL);
+ if (!mult)
+ goto err_free_gate;
+
+ mult->reg = reg;
+ mult->shift = SUN4I_A10_PLL3_DIV_SHIFT;
+ mult->width = SUN4I_A10_PLL3_DIV_WIDTH;
+ mult->lock = &sun4i_a10_pll3_lock;
+
+ clk = clk_register_composite(NULL, clk_name,
+ &parent, 1,
+ NULL, NULL,
+ &mult->hw, &clk_multiplier_ops,
+ &gate->hw, &clk_gate_ops,
+ 0);
+ if (IS_ERR(clk)) {
+ pr_err("%s: Couldn't register the clock\n", clk_name);
+ goto err_free_mult;
+ }
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (WARN_ON(ret))
+ goto err_clk_unregister;
+
+ return;
+
+err_clk_unregister:
+ clk_unregister_composite(clk);
+err_free_mult:
+ kfree(mult);
+err_free_gate:
+ kfree(gate);
+}
+
+CLK_OF_DECLARE(sun4i_a10_pll3, "allwinner,sun4i-a10-pll3-clk",
+ sun4i_a10_pll3_setup);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-01-15 04:10 +0100 |
| Subject | Re: [PATCH v2 06/26] clk: sunxi: Add PLL3 clock |
| Message-ID | <qR2VY-8p8-11@gated-at.bofh.it> |
| In reply to | #1309403 |
On Thu, Jan 14, 2016 at 04:24:49PM +0100, Maxime Ripard wrote: > The A10 SoCs and relatives have a PLL controller to drive the PLL3 and > PLL7, clocked from a 3MHz oscillator, that drives the display related > clocks (GPU, display engine, TCON, etc.) > > Add a driver for it. > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > Documentation/devicetree/bindings/clock/sunxi.txt | 1 + Acked-by: Rob Herring <robh@kernel.org> > drivers/clk/sunxi/Makefile | 1 + > drivers/clk/sunxi/clk-sun4i-pll3.c | 90 +++++++++++++++++++++++ > 3 files changed, 92 insertions(+) > create mode 100644 drivers/clk/sunxi/clk-sun4i-pll3.c
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:30 +0100 |
| Subject | [PATCH v2 04/26] clk: composite: Add unregister function |
| Message-ID | <qQS0z-uX-55@gated-at.bofh.it> |
| In reply to | #1309387 |
The composite clock didn't have any unregistration function, which forced
us to use clk_unregister directly on it.
While it was already not great from an API point of view, it also meant
that we were leaking the clk_composite structure allocated in
clk_register_composite.
Add a clk_unregister_composite function to fix this.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/clk/clk-composite.c | 15 +++++++++++++++
include/linux/clk-provider.h | 1 +
2 files changed, 16 insertions(+)
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 4735de0660cc..f4c38f209594 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -288,3 +288,18 @@ err:
kfree(composite);
return clk;
}
+
+void clk_unregister_composite(struct clk *clk)
+{
+ struct clk_composite *composite;
+ struct clk_hw *hw;
+
+ hw = __clk_get_hw(clk);
+ if (!hw)
+ return;
+
+ composite = to_clk_composite(hw);
+
+ clk_unregister(clk);
+ kfree(composite);
+}
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 0c55a37cf8f7..76c34a9622db 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -632,6 +632,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags);
+void clk_unregister_composite(struct clk *clk);
/***
* struct clk_gpio_gate - gpio gated clock
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:40 +0100 |
| Subject | [PATCH v2 02/26] reset: Make reset_control_ops const |
| Message-ID | <qQSad-yO-1@gated-at.bofh.it> |
| In reply to | #1309387 |
The ops pointer is holding a pointer to a structure that is usually not
modified. Make it const.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
include/linux/reset-controller.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index ce6b962ffed4..a3a5bcdb1d02 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -38,7 +38,7 @@ struct of_phandle_args;
* @nr_resets: number of reset controls in this reset controller device
*/
struct reset_controller_dev {
- struct reset_control_ops *ops;
+ const struct reset_control_ops *ops;
struct module *owner;
struct list_head list;
struct device_node *of_node;
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:40 +0100 |
| Subject | [PATCH v2 07/26] clk: sunxi: Add TCON channel1 clock |
| Message-ID | <qQSad-yO-3@gated-at.bofh.it> |
| In reply to | #1309387 |
The TCON is a controller generating the timings to output videos signals,
acting like both a CRTC and an encoder.
It has two channels depending on the output, each channel being driven by
its own clock (and own clock controller).
Add a driver for the channel 1 clock.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
Documentation/devicetree/bindings/clock/sunxi.txt | 1 +
drivers/clk/sunxi/Makefile | 1 +
drivers/clk/sunxi/clk-sun4i-tcon-ch1.c | 154 ++++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index bb9fb78dcff8..fe34fc56e803 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -67,6 +67,7 @@ Required properties:
"allwinner,sun7i-a20-out-clk" - for the external output clocks
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
"allwinner,sun4i-a10-tcon-ch0-clk" - for the TCON channel 0 clock on the A10
+ "allwinner,sun4i-a10-tcon-ch1-clk" - for the TCON channel 1 clock on the A10
"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
"allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
"allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 40c32ffd912c..0a20873cd103 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -12,6 +12,7 @@ obj-y += clk-mod0.o
obj-y += clk-simple-gates.o
obj-y += clk-sun4i-display.o
obj-y += clk-sun4i-pll3.o
+obj-y += clk-sun4i-tcon-ch1.o
obj-y += clk-sun8i-mbus.o
obj-y += clk-sun9i-core.o
obj-y += clk-sun9i-mmc.o
diff --git a/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
new file mode 100644
index 000000000000..51ddc38821f7
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2015 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define SUN4I_TCON_CH1_SCLK_NAME_LEN 32
+
+#define SUN4I_A10_TCON_CH1_SCLK2_PARENTS 4
+
+#define SUN4I_A10_TCON_CH1_SCLK2_GATE_BIT 31
+#define SUN4I_A10_TCON_CH1_SCLK2_MUX_MASK 3
+#define SUN4I_A10_TCON_CH1_SCLK2_MUX_SHIFT 24
+#define SUN4I_A10_TCON_CH1_SCLK2_DIV_WIDTH 4
+#define SUN4I_A10_TCON_CH1_SCLK2_DIV_SHIFT 0
+
+#define SUN4I_A10_TCON_CH1_SCLK1_GATE_BIT 15
+#define SUN4I_A10_TCON_CH1_SCLK1_DIV_WIDTH 1
+#define SUN4I_A10_TCON_CH1_SCLK1_DIV_SHIFT 11
+
+static DEFINE_SPINLOCK(sun4i_a10_tcon_ch1_lock);
+
+static void __init sun4i_a10_tcon_ch1_setup(struct device_node *node)
+{
+ const char *sclk2_parents[SUN4I_A10_TCON_CH1_SCLK2_PARENTS];
+ const char *sclk1_name = node->name;
+ const char *sclk2_name;
+ struct clk_divider *sclk1_div, *sclk2_div;
+ struct clk_gate *sclk1_gate, *sclk2_gate;
+ struct clk_mux *sclk2_mux;
+ struct clk *sclk1, *sclk2;
+ void __iomem *reg;
+ int i, ret;
+
+ of_property_read_string(node, "clock-output-names",
+ &sclk1_name);
+
+ sclk2_name = kasprintf(GFP_KERNEL, "%s2", sclk1_name);
+ if (!sclk2_name)
+ return;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n", sclk2_name);
+ return;
+ }
+
+ for (i = 0; i < SUN4I_A10_TCON_CH1_SCLK2_PARENTS; i++)
+ sclk2_parents[i] = of_clk_get_parent_name(node, i);
+
+ sclk2_mux = kzalloc(sizeof(*sclk2_mux), GFP_KERNEL);
+ if (!sclk2_mux)
+ return;
+
+ sclk2_mux->reg = reg;
+ sclk2_mux->shift = SUN4I_A10_TCON_CH1_SCLK2_MUX_SHIFT;
+ sclk2_mux->mask = SUN4I_A10_TCON_CH1_SCLK2_MUX_MASK;
+ sclk2_mux->lock = &sun4i_a10_tcon_ch1_lock;
+
+ sclk2_gate = kzalloc(sizeof(*sclk2_gate), GFP_KERNEL);
+ if (!sclk2_gate)
+ goto free_sclk2_mux;
+
+ sclk2_gate->reg = reg;
+ sclk2_gate->bit_idx = SUN4I_A10_TCON_CH1_SCLK2_GATE_BIT;
+ sclk2_gate->lock = &sun4i_a10_tcon_ch1_lock;
+
+ sclk2_div = kzalloc(sizeof(*sclk2_div), GFP_KERNEL);
+ if (!sclk2_div)
+ goto free_sclk2_gate;
+
+ sclk2_div->reg = reg;
+ sclk2_div->shift = SUN4I_A10_TCON_CH1_SCLK2_DIV_SHIFT;
+ sclk2_div->width = SUN4I_A10_TCON_CH1_SCLK2_DIV_WIDTH;
+ sclk2_div->lock = &sun4i_a10_tcon_ch1_lock;
+
+ sclk2 = clk_register_composite(NULL, sclk2_name, sclk2_parents,
+ SUN4I_A10_TCON_CH1_SCLK2_PARENTS,
+ &sclk2_mux->hw, &clk_mux_ops, &
+ sclk2_div->hw, &clk_divider_ops,
+ &sclk2_gate->hw, &clk_gate_ops,
+ 0);
+ if (IS_ERR(sclk2)) {
+ pr_err("%s: Couldn't register the clock\n", sclk2_name);
+ goto free_sclk2_div;
+ }
+
+ sclk1_div = kzalloc(sizeof(*sclk1_div), GFP_KERNEL);
+ if (!sclk1_div)
+ goto free_sclk2;
+
+ sclk1_div->reg = reg;
+ sclk1_div->shift = SUN4I_A10_TCON_CH1_SCLK1_DIV_SHIFT;
+ sclk1_div->width = SUN4I_A10_TCON_CH1_SCLK1_DIV_WIDTH;
+ sclk1_div->lock = &sun4i_a10_tcon_ch1_lock;
+
+ sclk1_gate = kzalloc(sizeof(*sclk1_gate), GFP_KERNEL);
+ if (!sclk1_gate)
+ goto free_sclk1_mux;
+
+ sclk1_gate->reg = reg;
+ sclk1_gate->bit_idx = SUN4I_A10_TCON_CH1_SCLK1_GATE_BIT;
+ sclk1_gate->lock = &sun4i_a10_tcon_ch1_lock;
+
+ sclk1 = clk_register_composite(NULL, sclk1_name, &sclk2_name, 1,
+ NULL, NULL,
+ &sclk1_div->hw, &clk_divider_ops,
+ &sclk1_gate->hw, &clk_gate_ops,
+ 0);
+ if (IS_ERR(sclk1)) {
+ pr_err("%s: Couldn't register the clock\n", sclk1_name);
+ goto free_sclk1_gate;
+ }
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, sclk1);
+ if (WARN_ON(ret))
+ goto free_sclk1;
+
+ return;
+
+free_sclk1:
+ clk_unregister_composite(sclk1);
+free_sclk1_gate:
+ kfree(sclk1_gate);
+free_sclk1_mux:
+ kfree(sclk1_div);
+free_sclk2:
+ clk_unregister(sclk2);
+free_sclk2_div:
+ kfree(sclk2_div);
+free_sclk2_gate:
+ kfree(sclk2_gate);
+free_sclk2_mux:
+ kfree(sclk2_mux);
+}
+
+CLK_OF_DECLARE(sun4i_a10_tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",
+ sun4i_a10_tcon_ch1_setup);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-01-15 04:10 +0100 |
| Subject | Re: [PATCH v2 07/26] clk: sunxi: Add TCON channel1 clock |
| Message-ID | <qR2VY-8p8-9@gated-at.bofh.it> |
| In reply to | #1309407 |
On Thu, Jan 14, 2016 at 04:24:50PM +0100, Maxime Ripard wrote: > The TCON is a controller generating the timings to output videos signals, > acting like both a CRTC and an encoder. > > It has two channels depending on the output, each channel being driven by > its own clock (and own clock controller). > > Add a driver for the channel 1 clock. > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > Documentation/devicetree/bindings/clock/sunxi.txt | 1 + Acked-by: Rob Herring <robh@kernel.org> > drivers/clk/sunxi/Makefile | 1 + > drivers/clk/sunxi/clk-sun4i-tcon-ch1.c | 154 ++++++++++++++++++++++ > 3 files changed, 156 insertions(+) > create mode 100644 drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:40 +0100 |
| Subject | [PATCH v2 26/26] DO NOT MERGE: ARM: sun5i: chip: Enable the LCD panel |
| Message-ID | <qQSad-yO-13@gated-at.bofh.it> |
| In reply to | #1309387 |
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun5i-r8-chip.dts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-r8-chip.dts b/arch/arm/boot/dts/sun5i-r8-chip.dts
index c26c095b42c6..147c39106f63 100644
--- a/arch/arm/boot/dts/sun5i-r8-chip.dts
+++ b/arch/arm/boot/dts/sun5i-r8-chip.dts
@@ -82,6 +82,10 @@
vin0-supply = <®_ldo3>;
vin1-supply = <®_ldo4>;
};
+
+ panel: panel {
+ compatible = "olimex,lcd-olinuxino-43-ts";
+ };
};
&be0 {
@@ -236,6 +240,7 @@
&tcon0 {
status = "okay";
+ allwinner,panel = <&panel>;
};
&tve0 {
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|---|
| Date | 2016-01-14 16:40 +0100 |
| Subject | [PATCH v2 15/26] drm/modes: Support modes names on the command line |
| Message-ID | <qQSae-yO-23@gated-at.bofh.it> |
| In reply to | #1309387 |
The drm subsystem also uses the video= kernel parameter, and in the
documentation refers to the fbdev documentation for that parameter.
However, that documentation also says that instead of giving the mode using
its resolution we can also give a name. However, DRM doesn't handle that
case at the moment. Even though in most case it shouldn't make any
difference, it might be useful for analog modes, where different standards
might have the same resolution, but still have a few different parameters
that are not encoded in the modes (NTSC vs NTSC-J vs PAL-M for example).
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/gpu/drm/drm_crtc.c | 3 ++-
drivers/gpu/drm/drm_fb_helper.c | 4 ++++
drivers/gpu/drm/drm_modes.c | 49 ++++++++++++++++++++++++++++-------------
include/drm/drm_modes.h | 1 +
4 files changed, 41 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 24c5434abd1c..c3267d695e4e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -846,8 +846,9 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
connector->force = mode->force;
}
- DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+ DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n",
connector->name,
+ mode->name ? mode->name : "",
mode->xres, mode->yres,
mode->refresh_specified ? mode->refresh : 60,
mode->rb ? " reduced blanking" : "",
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 69cbab5e5c81..fcb7ba796202 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1649,6 +1649,10 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
prefer_non_interlace = !cmdline_mode->interlace;
again:
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
+ /* Check (optional) mode name first */
+ if (!strcmp(mode->name, cmdline_mode->name))
+ return mode;
+
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
mode->vdisplay != cmdline_mode->yres)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 5b1921b22dd6..bbb23000eb5b 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1351,7 +1351,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
struct drm_cmdline_mode *mode)
{
const char *name;
- bool parse_extras = false;
+ bool named_mode = false, parse_extras = false;
unsigned int bpp_off = 0, refresh_off = 0;
unsigned int mode_end = 0;
char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
@@ -1370,8 +1370,14 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
name = mode_option;
+ /*
+ * If the first character is not a digit, then it means that
+ * we have a named mode.
+ */
if (!isdigit(name[0]))
- return false;
+ named_mode = true;
+ else
+ named_mode = false;
/* Try to locate the bpp and refresh specifiers, if any */
bpp_ptr = strchr(name, '-');
@@ -1398,12 +1404,16 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
parse_extras = true;
}
- ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
- parse_extras,
- connector,
- mode);
- if (ret)
- return false;
+ if (named_mode) {
+ strncpy(mode->name, name, mode_end);
+ } else {
+ ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
+ parse_extras,
+ connector,
+ mode);
+ if (ret)
+ return false;
+ }
mode->specified = true;
if (bpp_ptr) {
@@ -1431,14 +1441,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
extra_ptr = refresh_end_ptr;
if (extra_ptr) {
- int remaining = strlen(name) - (extra_ptr - name);
+ if (!named_mode) {
+ int len = strlen(name) - (extra_ptr - name);
- /*
- * We still have characters to process, while
- * we shouldn't have any
- */
- if (remaining > 0)
- return false;
+ ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
+ connector, mode);
+ if (ret)
+ return false;
+ } else {
+ int remaining = strlen(name) - (extra_ptr - name);
+
+ /*
+ * We still have characters to process, while
+ * we shouldn't have any
+ */
+ if (remaining > 0)
+ return false;
+ }
}
return true;
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index 08a8cac9e555..709d43f3515e 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -151,6 +151,7 @@ struct drm_display_mode {
/* mode specified on the command line */
struct drm_cmdline_mode {
+ char name[DRM_DISPLAY_MODE_LEN];
bool specified;
bool refresh_specified;
bool bpp_specified;
--
2.6.4
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web