* [PATCH 2/6] ASoC: samsung: smdk_wm8580: Remove old platforms and drop mach-types usage
From: Lars-Peter Clausen @ 2016-11-19 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8248c49a-1108-7fce-ed99-01653e11c670@metafoo.de>
On 11/19/2016 04:42 PM, Lars-Peter Clausen wrote:
> On 11/19/2016 03:48 PM, Krzysztof Kozlowski wrote:
> [...]
>> @@ -206,15 +204,10 @@ static int __init smdk_audio_init(void)
>> int ret;
>> char *str;
>>
>> - if (machine_is_smdkc100()
>> - || machine_is_smdkv210() || machine_is_smdkc110()) {
>> - smdk.num_links = 3;
>> - } else if (machine_is_smdk6410()) {
>> - str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
>> - str[strlen(str) - 1] = '2';
>> - str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
>> - str[strlen(str) - 1] = '2';
>> - }
>> + str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
>> + str[strlen(str) - 1] = '2';
>> + str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
>> + str[strlen(str) - 1] = '2';
>
> This could be further simplified by just updating the initial cpu_dai_name
> string in the dai_link struct.
>
> Especially considering that the cpu_dai_name is a string literal and the ARM
> kernel now has rodata write protection enabled by default, so modifying it
> will crash the kernel.
Spoke too soon, you fix this up in the next patch. But I'd just squash that
change into this patch. I think it is pretty safe to assume that it is correct.
^ permalink raw reply
* [PATCH 2/6] ASoC: samsung: smdk_wm8580: Remove old platforms and drop mach-types usage
From: Lars-Peter Clausen @ 2016-11-19 15:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <90e0f455-fa24-f643-fa07-67efe217188a@metafoo.de>
On 11/19/2016 04:45 PM, Lars-Peter Clausen wrote:
> On 11/19/2016 04:42 PM, Lars-Peter Clausen wrote:
>> On 11/19/2016 03:48 PM, Krzysztof Kozlowski wrote:
>> [...]
>>> @@ -206,15 +204,10 @@ static int __init smdk_audio_init(void)
>>> int ret;
>>> char *str;
>>>
>>> - if (machine_is_smdkc100()
>>> - || machine_is_smdkv210() || machine_is_smdkc110()) {
>>> - smdk.num_links = 3;
>>> - } else if (machine_is_smdk6410()) {
>>> - str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
>>> - str[strlen(str) - 1] = '2';
>>> - str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
>>> - str[strlen(str) - 1] = '2';
>>> - }
>>> + str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
>>> + str[strlen(str) - 1] = '2';
>>> + str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
>>> + str[strlen(str) - 1] = '2';
>>
>> This could be further simplified by just updating the initial cpu_dai_name
>> string in the dai_link struct.
>>
>> Especially considering that the cpu_dai_name is a string literal and the ARM
>> kernel now has rodata write protection enabled by default, so modifying it
>> will crash the kernel.
>
> Spoke too soon, you fix this up in the next patch. But I'd just squash that
> change into this patch. I think it is pretty safe to assume that it is correct.
>
And another thing. Since num_links is always 2 now the last entry from the
smdk_dai array can be removed and num_links can be initialized using
ARRAY_SIZE().
^ permalink raw reply
* [GIT PULL] Allwinner late DT changes for 4.10
From: Olof Johansson @ 2016-11-19 17:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACRpkdbw3AUj510wPGFG55cKnOyEOX7Syz1BnJx-ygo1eAqzGg@mail.gmail.com>
On Sat, Nov 19, 2016 at 7:28 AM, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Sat, Nov 19, 2016 at 1:27 AM, Olof Johansson <olof@lixom.net> wrote:
>
>> Also, this won't work since this branch does not contain the required
>> pinctrl changes. If we merge this without basing it on those changes we lose
>> bisectability.
>
> I usually operate on the assumption that arch/*/boot/dts/* and drivers/*
> do not need to be boot-time bisectable sync:ed, because of the ambition
> to maintaining DTS files outside of the kernel in the long run, and at that
> point they would be versioned orthogonally anyways.
>
> On the other hand, that does look like a pipe dream, so maybe I should
> just stop pretending.
For new features/drivers/platforms that is definitely the case: Merge
code through the suitable tree and it'll come together when the code
is all merged.
For a cleanup it's a bit different, since you shouldn't regress and
break existing support on the individual branches. I.e. it's OK to not
make things work for the first time until all comes together, but
things that is already working shouldn't break.
-Olof
^ permalink raw reply
* [PATCH] coresight: perf: Add a missing call to etm_free_aux
From: Quentin Lambert @ 2016-11-19 17:41 UTC (permalink / raw)
To: linux-arm-kernel
Most error branches following the call to alloc_event_data contain a call to
etm_free_aux. This patch add a call to etm_free_aux to an error branch
that does not call it.
This issue was found with Hector.
Signed-off-by: Quentin Lambert <lambert.quentin@gmail.com>
---
drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -215,7 +215,7 @@ static void *etm_setup_aux(int event_cpu
*/
sink = coresight_get_enabled_sink(true);
if (!sink)
- return NULL;
+ goto err;
INIT_WORK(&event_data->work, free_event_data);
^ permalink raw reply
* [PATCH 2/6] ASoC: samsung: smdk_wm8580: Remove old platforms and drop mach-types usage
From: Krzysztof Kozlowski @ 2016-11-19 18:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <9ef6c39c-e7a2-4591-380f-07f92317313b@metafoo.de>
On Sat, Nov 19, 2016 at 04:48:26PM +0100, Lars-Peter Clausen wrote:
> On 11/19/2016 04:45 PM, Lars-Peter Clausen wrote:
> > On 11/19/2016 04:42 PM, Lars-Peter Clausen wrote:
> >> On 11/19/2016 03:48 PM, Krzysztof Kozlowski wrote:
> >> [...]
> >>> @@ -206,15 +204,10 @@ static int __init smdk_audio_init(void)
> >>> int ret;
> >>> char *str;
> >>>
> >>> - if (machine_is_smdkc100()
> >>> - || machine_is_smdkv210() || machine_is_smdkc110()) {
> >>> - smdk.num_links = 3;
> >>> - } else if (machine_is_smdk6410()) {
> >>> - str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
> >>> - str[strlen(str) - 1] = '2';
> >>> - str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
> >>> - str[strlen(str) - 1] = '2';
> >>> - }
> >>> + str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
> >>> + str[strlen(str) - 1] = '2';
> >>> + str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
> >>> + str[strlen(str) - 1] = '2';
> >>
> >> This could be further simplified by just updating the initial cpu_dai_name
> >> string in the dai_link struct.
> >>
> >> Especially considering that the cpu_dai_name is a string literal and the ARM
> >> kernel now has rodata write protection enabled by default, so modifying it
> >> will crash the kernel.
> >
> > Spoke too soon, you fix this up in the next patch. But I'd just squash that
> > change into this patch. I think it is pretty safe to assume that it is correct.
Yes, I wanted to split trivial change from something which would be nice
to test (I did not test it). However you're right that logically this is
the same change.
> And another thing. Since num_links is always 2 now the last entry from the
> smdk_dai array can be removed and num_links can be initialized using
> ARRAY_SIZE().
Ahh, indeed. The third DAI link (SEC_PLAYBACK) could be removed now.
Thanks for feedback,
Krzysztof
^ permalink raw reply
* [PATCH] coresight: perf: Add a missing call to etm_free_aux
From: Quentin Lambert @ 2016-11-19 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161119174124.20136-1-lambert.quentin@gmail.com>
On 11/19/2016 06:41 PM, Quentin Lambert wrote:
> Most error branches following the call to alloc_event_data contain a call to
> etm_free_aux. This patch add a call to etm_free_aux to an error branch
> that does not call it.
>
> This issue was found with Hector.
>
> Signed-off-by: Quentin Lambert <lambert.quentin@gmail.com>
> ---
> drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> --- a/drivers/hwtracing/coresight/coresight-etm-perf.c
> +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
> @@ -215,7 +215,7 @@ static void *etm_setup_aux(int event_cpu
> */
> sink = coresight_get_enabled_sink(true);
> if (!sink)
> - return NULL;
> + goto err;
>
> INIT_WORK(&event_data->work, free_event_data);
>
I realized that I hadn't try to compile after having sent this patch and
I wasn't able to compile it.
Therefore, please ignore it for now.
Quentin
^ permalink raw reply
* [PATCH] arm: spin one more cycle in timer-based delays
From: Mason @ 2016-11-19 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161119110301.GQ1041@n2100.armlinux.org.uk>
On 19/11/2016 12:03, Russell King - ARM Linux wrote:
> Linus, please see the comment and patch at the bottom of this mail.
> Thanks.
>
> On Sat, Nov 19, 2016 at 12:47:02PM +0530, Afzal Mohammed wrote:
>> Hi Mason,
>>
>> On Fri, Nov 18, 2016 at 03:18:58PM +0100, Mason wrote:
>>> On 18/11/2016 13:54, Russell King - ARM Linux wrote:
>>
>>>> So, NAK on this change. udelay is not super-accurate.
>>>
>>> usleep_range() fixed this issue recently.
>>> 6c5e9059692567740a4ee51530dffe51a4b9584d
>>> https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?h=timers/core&id=6c5e9059692567740a4ee51530dffe51a4b9584d
>>
>> But the above "timers: Fix usleep_range() in the context of
>> wake_up_process()" is to avoid wakeup causing premature return than
>> about being precise, no ?
>
> usleep*() is different from udelay(). usleep*() is not based on looping
> a certain number of times, and doesn't involve calibration of such a
> loop.
You keep saying that udelay is loop-based. That's merely the fall-back,
when nothing better is available. As you know, there are several better
arch-specific options available (peterz described some on x86).
On my platform, udelay polls a platform tick counter. (In fact, you
were the one to recommend that solution to me years ago). This tick
counter is tied to a high-precision crystal, the error of which is
measured in parts per million. The memory bus to this device offers
some guarantees for the access latency.
IIUC, arm and arm64 even have an architected counter that is
guaranteed to tick at constant frequency, and is accessible
without leaving the CPU core.
> usleep*() is based on the scheduler, which has tighter
> requirements laid down in POSIX amongst other standards, such as "not
> timing out before this specified time" (due to things like select(),
> poll(), etc.) udelay() is purely a kernel thing, unspecified by any
> standard.
The "problem" with udelay is that the same API exists on every single
kernel ever written, and users have implicit expectations about the
implementation. (Principle of least astonishment)
>> With conflicting opinion on delay/sleep fn's from the players, the one
>> in gallery would get confused.
>>
>> But Linus has mentioned udelay as not meant to be precise, okay ?
>
> Exactly - and the reason for that (as I've explained several times in
> the past) the "standard" software delay loop calibrated against the
> timer interrupt is _always_ going to be short.
OK, so loop-based delays are known to be short. Would you or Linus
accept a patch that adds a X% cushion *in the implementation* ?
You are saying "people shouldn't expect udelay(10) to delay at least
10 ?s, thus they should write udelay(10+N)".
Why not hide that implementation detail inside the implementation,
so as not to force the pessimization on every other implementation
behind the udelay/ndelay wrapper?
void loop_based_udelay(long us) {
spin_for_some_us(us + us/8);
}
> I explain why this is in the message to which Linus replied:
>
> http://lists.openwall.net/linux-kernel/2011/01/09/56
>
> A consequence of the formula that I give in (2) in that mail is that
> the higher the HZ value, the more error in the resulting value of
> loops_per_jiffy, and the shorter udelay(1) than 1us will be, since
> "timer_interrupt_usec" is a constant but "usec_per_jiffy" reduces.
>
> So folk need to put the idea that "udelay(1) will always be at least
> 1us" out of their minds - that's simply not guaranteed by the kernel.
> Linus' reply also indicates that we don't care if it's out by 5%,
> and probably more than that too.
>
> If someone can show that our timer-based udelay() produces an error
> more than 5%, then I'll apply the patch.
I gave an example where ndelay had a 60% error (37 instead of 100 ns).
If one is using a 1 MHz clock for timer-based delays, udelay(1)
will randomly return immediately (100% error).
> What I don't want to do is
> to apply the patch because someone thinks that udelay() should not
> return early. Applying it in that case has the effect of re-inforcing
> what is an incorrect assumption, leading to people writing buggy drivers
> that have delays which are too finely "tuned" - which may work with a
> timer-based udelay() but cause failures with a loop-based udelay().
Again, I think it would be much smarter to hide this quirk within
the loop-based delay implementation.
Why is it unacceptable to "fix" the API, instead of fixing the
expectations of driver writers?
> This is all about ensuring that driver authors do the right thing.
What is the rationale behind the devm managed resources?
Driver writers were getting things wrong, and the kernel
provided a framework to help them with the hard part.
Likewise, instead of setting them up to fail with a quirky
delay routine, why not help them by writing an implementation
to match their expectation?
> Linus, how about we add something like this to linux/delay.h to document
> this fact?
>
> include/linux/delay.h | 12 +++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/include/linux/delay.h b/include/linux/delay.h
> index a6ecb34cf547..2ecb3c46b20a 100644
> --- a/include/linux/delay.h
> +++ b/include/linux/delay.h
> @@ -5,6 +5,18 @@
> * Copyright (C) 1993 Linus Torvalds
> *
> * Delay routines, using a pre-computed "loops_per_jiffy" value.
> + *
> + * Please note that ndelay(), udelay() and mdelay() may return early for
> + * several reasons:
> + * 1. computed loops_per_jiffy too low (due to the time taken to
> + * execute the timer interrupt.)
> + * 2. cache behaviour affecting the time it takes to execute the
> + * loop function.
> + * 3. CPU clock rate changes.
> + * As a result, delays should always be over-stated.
> + *
> + * Please see this thread:
> + * http://lists.openwall.net/linux-kernel/2011/01/09/56
(None of the reasons you stated affect a tick-counter-based delay.)
If one wants a 100 ns delay, what should one write?
If one wants a 1 ?s delay, what should one write?
If one wants a 100 ?s delay, what should one write?
Is the relative error constant?
Is there a constant component in the error, independent of requested delay?
It seems to me you're saying "on platform A, udelay has 50%
error, so driver writers should write udelay(N+N/2);"
The code is now unnecessarily pessimized on hundreds of platform
that don't have that behavior.
Why not fix the implementation of that platform's udelay to
add the padding itself? That way, other platforms are not
hampered by that platform's ineptitude.
Regards.
^ permalink raw reply
* [PATCH] coresight: perf: Add a missing call to etm_free_aux
From: Quentin Lambert @ 2016-11-19 18:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3e105f1d-c984-358f-a996-b9d3a2d01e0d@gmail.com>
On 11/19/2016 07:22 PM, Quentin Lambert wrote:
>
> On 11/19/2016 06:41 PM, Quentin Lambert wrote:
>> Most error branches following the call to alloc_event_data contain a
>> call to
>> etm_free_aux. This patch add a call to etm_free_aux to an error branch
>> that does not call it.
>>
>> This issue was found with Hector.
>>
>> Signed-off-by: Quentin Lambert <lambert.quentin@gmail.com>
>> ---
>> drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> --- a/drivers/hwtracing/coresight/coresight-etm-perf.c
>> +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
>> @@ -215,7 +215,7 @@ static void *etm_setup_aux(int event_cpu
>> */
>> sink = coresight_get_enabled_sink(true);
>> if (!sink)
>> - return NULL;
>> + goto err;
>> INIT_WORK(&event_data->work, free_event_data);
> I realized that I hadn't try to compile after having sent this patch and
> I wasn't able to compile it.
> Therefore, please ignore it for now.
I have just confirmed that it compiles.
Quentin
^ permalink raw reply
* [PULL] KVM/ARM updates for 4.9-rc6
From: Radim Krčmář @ 2016-11-19 19:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479461966-20136-1-git-send-email-marc.zyngier@arm.com>
2016-11-18 09:39+0000, Marc Zyngier:
> Paolo, Radim,
>
> Please find below the pull request for a couple of fixes for the
> PMU emulation, courtesy of Wei. Both patches are candidates for stable.
Pulled, thanks.
^ permalink raw reply
* [PATCH] dmaengine: qcom_hidma: autoload while probing ACPI
From: Sinan Kaya @ 2016-11-19 19:28 UTC (permalink / raw)
To: linux-arm-kernel
MODULE_DEVICE_TABLE is used by the kernel to determine which device driver
should be loaded for which platform device. MODULE_DEVICE_TABLE has been
only defined for the device-tree based platforms in the current code.
Defining it also for ACPI based platforms.
Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
drivers/dma/qcom/hidma.c | 1 +
drivers/dma/qcom/hidma_mgmt.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
index 248e74b..3c982c9 100644
--- a/drivers/dma/qcom/hidma.c
+++ b/drivers/dma/qcom/hidma.c
@@ -895,6 +895,7 @@ static int hidma_remove(struct platform_device *pdev)
{"QCOM8062"},
{},
};
+MODULE_DEVICE_TABLE(acpi, hidma_acpi_ids);
#endif
static const struct of_device_id hidma_match[] = {
diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c
index 985f5ac..f847d32 100644
--- a/drivers/dma/qcom/hidma_mgmt.c
+++ b/drivers/dma/qcom/hidma_mgmt.c
@@ -282,6 +282,7 @@ static int hidma_mgmt_probe(struct platform_device *pdev)
{"QCOM8060"},
{},
};
+MODULE_DEVICE_TABLE(acpi, hidma_mgmt_acpi_ids);
#endif
static const struct of_device_id hidma_mgmt_match[] = {
--
1.9.1
^ permalink raw reply related
* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: tomas.hlavacek at nic.cz @ 2016-11-19 20:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161114202832.GG24546@lunn.ch>
Hello Uwe!
On Mon, Nov 14, 2016 at 9:28 PM, Andrew Lunn <andrew@lunn.ch> wrote:
>>
>> + i2c at 7 {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <7>;
>> +
>> + pcawan: gpio at 71 {
>> + compatible = "nxp,pca9538";
>> + reg = <0x71>;
>> +
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pcawan_pins>;
>> +
>> + interrupt-parent = <&gpio1>;
>> + interrupts = <14
>> IRQ_TYPE_LEVEL_LOW>;
>> +
>> + gpio-controller;
>> + #gpio-cells = <2>;
>> +
>> + interrupt-controller;
>> + #interrupt-cells = <2>;
>> + };
>> + };
>>
>> The interrupt-controller part doesn't seem to work though, at least
>>
>> + interrupt-parent = <&pcawan>;
>> + interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
>>
>> in the phy node gives an error.
>
> Interrupts don't seem to work very well with the nxp,pca9538. Which
> is probably why it is disabled by default.
I was thinking about this issue and I can remember that there was an
earlier prototype that had a shared interrupt line from PHY (88E1514)
and from the PCA9538. In this case we needed to specifically disable
the interrupt of the PHY to release the interrupt line (which needed a
hack into PHY driver code). The IRQ from PHY is connected as an
ordinary input to PCA9538 in later board prototype. And the same holds
for the production version.
Do you have CZ11NIC13 or older board revision?
Tomas
^ permalink raw reply
* [arm-soc:sunxi/dt64 3/3] arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi:45:46: fatal error: dt-bindings/clock/sun50i-a64-ccu.h: No such file or directory
From: kbuild test robot @ 2016-11-20 2:21 UTC (permalink / raw)
To: linux-arm-kernel
Hi Andre,
FYI, the error/warning still remains.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git sunxi/dt64
head: 4e3886081848b7ea16452a92c4324acaab644d49
commit: 4e3886081848b7ea16452a92c4324acaab644d49 [3/3] arm64: dts: add Pine64 support
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 4e3886081848b7ea16452a92c4324acaab644d49
# save the attached .config to linux build tree
make.cross ARCH=arm64
All errors (new ones prefixed by >>):
In file included from arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts:45:0,
from arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts:43:
>> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi:45:46: fatal error: dt-bindings/clock/sun50i-a64-ccu.h: No such file or directory
#include <dt-bindings/clock/sun50i-a64-ccu.h>
^
compilation terminated.
vim +45 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
6bc37fac Andre Przywara 2016-01-18 29 * Software is furnished to do so, subject to the following
6bc37fac Andre Przywara 2016-01-18 30 * conditions:
6bc37fac Andre Przywara 2016-01-18 31 *
6bc37fac Andre Przywara 2016-01-18 32 * The above copyright notice and this permission notice shall be
6bc37fac Andre Przywara 2016-01-18 33 * included in all copies or substantial portions of the Software.
6bc37fac Andre Przywara 2016-01-18 34 *
6bc37fac Andre Przywara 2016-01-18 35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
6bc37fac Andre Przywara 2016-01-18 36 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
6bc37fac Andre Przywara 2016-01-18 37 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
6bc37fac Andre Przywara 2016-01-18 38 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
6bc37fac Andre Przywara 2016-01-18 39 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
6bc37fac Andre Przywara 2016-01-18 40 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
6bc37fac Andre Przywara 2016-01-18 41 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
6bc37fac Andre Przywara 2016-01-18 42 * OTHER DEALINGS IN THE SOFTWARE.
6bc37fac Andre Przywara 2016-01-18 43 */
6bc37fac Andre Przywara 2016-01-18 44
6bc37fac Andre Przywara 2016-01-18 @45 #include <dt-bindings/clock/sun50i-a64-ccu.h>
6bc37fac Andre Przywara 2016-01-18 46 #include <dt-bindings/interrupt-controller/arm-gic.h>
6bc37fac Andre Przywara 2016-01-18 47 #include <dt-bindings/pinctrl/sun4i-a10.h>
6bc37fac Andre Przywara 2016-01-18 48 #include <dt-bindings/reset/sun50i-a64-ccu.h>
6bc37fac Andre Przywara 2016-01-18 49
6bc37fac Andre Przywara 2016-01-18 50 / {
6bc37fac Andre Przywara 2016-01-18 51 interrupt-parent = <&gic>;
6bc37fac Andre Przywara 2016-01-18 52 #address-cells = <1>;
6bc37fac Andre Przywara 2016-01-18 53 #size-cells = <1>;
:::::: The code at line 45 was first introduced by commit
:::::: 6bc37fac30cf01c39feb17834090089304bd1d31 arm64: dts: add Allwinner A64 SoC .dtsi
:::::: TO: Andre Przywara <andre.przywara@arm.com>
:::::: CC: Maxime Ripard <maxime.ripard@free-electrons.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 32759 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161120/3facb00b/attachment-0001.gz>
^ permalink raw reply
* [GIT PULL] ARM: SoC fixes
From: Olof Johansson @ 2016-11-20 2:22 UTC (permalink / raw)
To: linux-arm-kernel
Hi Linus,
The following changes since commit 4b3415c9363f828e7f1e45edecb57930849d5d5b:
Merge tag 'imx-fixes-3.19-2' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into fixes (2015-01-23 14:23:40 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git tags/armsoc-for-linus
for you to fetch changes up to 28111dda37e653781efc73c06229f006739b3982:
Merge tag 'renesas-soc-fixes3-for-v3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas into fixes (2015-02-01 08:51:12 -0800)
----------------------------------------------------------------
ARM: SoC fixes
One more week's worth of fixes. Worth pointing out here are:
- A patch fixing detaching of iommu registrations when a device is removed --
earlier the ops pointer wasn't managed properly
- Another set of Renesas boards get the same GIC setup fixup as others have in
previous -rcs
- Serial port aliases fixups for sunxi. We did the same to tegra but we
caught that in time before the merge window due to more machines being
affected. Here it took longer for anyone to notice.
- A couple more DT tweaks on sunxi
- A follow-up patch for the mvebu coherency disabling in last -rc batch
----------------------------------------------------------------
Chen-Yu Tsai (1):
ARM: dts: sunxi: Fix usb-phy support for sun4i/sun5i
Hans de Goede (2):
ARM: dts: sun6i: ippo-q8h-v5: Fix serial0 alias
ARM: dts: sun4i: Add simplefb node with de_fe0-de_be0-lcd0-hdmi pipeline
Laurent Pinchart (1):
arm: dma-mapping: Set DMA IOMMU ops in arm_iommu_attach_device()
Magnus Damm (2):
ARM: shmobile: r8a73a4: Instantiate GIC from C board code in legacy builds
ARM: shmobile: r8a7790: Instantiate GIC from C board code in legacy builds
Maxime Ripard (1):
ARM: sunxi: dt: Fix aliases
Olof Johansson (3):
Merge tag 'sunxi-fixes-for-3.19' of https://git.kernel.org/.../mripard/linux into fixes
Merge tag 'mvebu-fixes-3.19-6' of git://git.infradead.org/linux-mvebu into fixes
Merge tag 'renesas-soc-fixes3-for-v3.19' of git://git.kernel.org/.../horms/renesas into fixes
Thomas Petazzoni (1):
ARM: mvebu: don't set the PL310 in I/O coherency mode when I/O coherency is disabled
arch/arm/boot/dts/sun4i-a10.dtsi | 20 ++++-----
arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 6 +++
arch/arm/boot/dts/sun5i-a10s.dtsi | 8 +---
arch/arm/boot/dts/sun5i-a13-hsg-h702.dts | 4 ++
arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 4 ++
arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 4 ++
arch/arm/boot/dts/sun5i-a13.dtsi | 9 +---
arch/arm/boot/dts/sun6i-a31.dtsi | 6 ---
arch/arm/boot/dts/sun7i-a20-bananapi.dts | 6 +++
arch/arm/boot/dts/sun7i-a20-hummingbird.dts | 8 ++++
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 3 ++
arch/arm/boot/dts/sun7i-a20.dtsi | 8 ----
arch/arm/boot/dts/sun8i-a23-ippo-q8h-v5.dts | 4 ++
arch/arm/boot/dts/sun8i-a23.dtsi | 9 ----
arch/arm/boot/dts/sun9i-a80-optimus.dts | 5 +++
arch/arm/boot/dts/sun9i-a80.dtsi | 10 -----
arch/arm/mach-mvebu/coherency.c | 7 ++++
arch/arm/mach-shmobile/board-ape6evm.c | 20 +++++++++
arch/arm/mach-shmobile/board-lager.c | 13 ++++++
arch/arm/mach-shmobile/setup-rcar-gen2.c | 2 +
arch/arm/mach-shmobile/timer.c | 12 ++++++
arch/arm/mm/dma-mapping.c | 53 +++++++++++++++++-------
22 files changed, 150 insertions(+), 71 deletions(-)
^ permalink raw reply
* [GIT PULL] ARM: SoC fixes
From: Linus Torvalds @ 2016-11-20 2:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161120022232.GA24427@quad.lixom.net>
On Sat, Nov 19, 2016 at 6:22 PM, Olof Johansson <olof@lixom.net> wrote:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git tags/armsoc-for-linus
Forgot to push? I get a tag that is an old one from August last year..
Linus
^ permalink raw reply
* [GIT PULL] ARM: SoC fixes
From: Olof Johansson @ 2016-11-20 2:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CA+55aFwxuE7Y3-GB1eRLk2Q6QGf1348VN3D8T2T=YaNx0QVzmQ@mail.gmail.com>
On Sat, Nov 19, 2016 at 6:26 PM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
> On Sat, Nov 19, 2016 at 6:22 PM, Olof Johansson <olof@lixom.net> wrote:
>>
>> git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git tags/armsoc-for-linus
>
> Forgot to push? I get a tag that is an old one from August last year..
Crap. Inlined the wrong generated pull request that indeed was from
last year. Fresh one coming in separate email.
-Olof
^ permalink raw reply
* [GIT PULL] ARM: SoC fixes
From: Olof Johansson @ 2016-11-20 2:37 UTC (permalink / raw)
To: linux-arm-kernel
Hi Linus,
Actual current version this time. Had an old file with the generated pull
request around in my directory on this machine.
Thanks,
-Olof
The following changes since commit a25f0944ba9b1d8a6813fd6f1a86f1bd59ac25a6:
Linux 4.9-rc5 (2016-11-13 10:32:32 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git tags/armsoc-fixes
for you to fetch changes up to 9883ed4433b358528e1a41e56ae01a4b02a1dde3:
Merge tag 'sunxi-fixes-for-4.9' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into fixes (2016-11-17 16:43:38 -0800)
----------------------------------------------------------------
ARM: SoC fixes for v4.9-rc
Again a set of smaller fixes across several platforms (OMAP, Marvell,
Allwinner, i.MX, etc).
A handful of typo fixes and smaller missing contents from device trees,
with some tweaks to OMAP mach files to deal with CPU feature print
misformatting, potential NULL ptr dereference and one setup issue
with UARTs.
----------------------------------------------------------------
Adam Ford (2):
ARM: dts: omap3: Fix memory node in Torpedo board
ARM: omap3: Add missing memory node in SOM-LV
Colin Ian King (1):
ARM: OMAP2+: PRM: initialize en_uart4_mask and grpsel_uart4_mask
C?dric Le Goater (1):
ipmi/bt-bmc: change compatible node to 'aspeed, ast2400-ibt-bmc'
Dave Gerlach (1):
ARM: AM43XX: Select OMAP_INTERCONNECT in Kconfig
Fabio Estevam (1):
ARM: dts: imx53-qsb: Fix regulator constraints
Gregory CLEMENT (1):
arm64: dts: marvell: Fix typo in label name on Armada 37xx
H. Nikolaus Schaller (4):
dts: omap5: board-common: add phandle to reference Palmas gpadc
dts: omap5: board-common: enable twl6040 headset jack detection
ASoC: omap-abe-twl6040: fix typo in bindings documentation
ARM: dts: omap5: board-common: fix wrong SMPS6 (VDD-DDR3) voltage
Icenowy Zheng (1):
ARM: dts: sun8i: fix the pinmux for UART1
Loic Pallardy (1):
ARM: dts: STiH410-b2260: Fix typo in spi0 chipselect definition
Marcin Wojtas (2):
arm64: dts: marvell: fix clocksource for CP110 slave SPI0
arm64: dts: marvell: add unique identifiers for Armada A8k SPI controllers
Nicolae Rosia (1):
ARM: OMAP2+: avoid NULL pointer dereference
Olof Johansson (5):
Merge tag 'mvebu-fixes-4.9-1' of git://git.infradead.org/linux-mvebu into fixes
Merge tag 'omap-for-v4.9/fixes-for-rc-cycle' of git://git.kernel.org/.../tmlind/linux-omap into fixes
Merge tag 'imx-fixes-4.9-2' of git://git.kernel.org/.../shawnguo/linux into fixes
Merge tag 'sti-dt-for-v4.9-rc' of git://git.kernel.org/.../pchotard/sti into fixes
Merge tag 'sunxi-fixes-for-4.9' of https://git.kernel.org/.../mripard/linux into fixes
Tony Lindgren (1):
ARM: OMAP3: Fix formatting of features printed
...eed,ast2400-bt-bmc.txt => aspeed,ast2400-ibt-bmc.txt} | 4 ++--
.../devicetree/bindings/sound/omap-abe-twl6040.txt | 2 +-
arch/arm/boot/dts/imx53-qsb.dts | 14 +++++++-------
arch/arm/boot/dts/logicpd-som-lv.dtsi | 5 +++++
arch/arm/boot/dts/logicpd-torpedo-som.dtsi | 4 ++--
arch/arm/boot/dts/omap5-board-common.dtsi | 7 ++++---
arch/arm/boot/dts/stih410-b2260.dts | 2 +-
arch/arm/boot/dts/sun8i-a23-a33.dtsi | 4 ++++
arch/arm/mach-omap2/Kconfig | 1 +
arch/arm/mach-omap2/id.c | 16 +++++++++++-----
arch/arm/mach-omap2/prm3xxx.c | 3 +++
arch/arm/mach-omap2/voltage.c | 6 ++++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 4 ++--
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +++---
drivers/char/ipmi/bt-bmc.c | 4 ++--
15 files changed, 54 insertions(+), 28 deletions(-)
rename Documentation/devicetree/bindings/ipmi/{aspeed,ast2400-bt-bmc.txt => aspeed,ast2400-ibt-bmc.txt} (85%)
^ permalink raw reply
* commit 4dd1837d7589f468ed109556513f476e7a7f9121 breaks build
From: Tobias Jakobi @ 2016-11-20 2:47 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
this is a resend of my initial mail, see below, to Al Viro (which sadly
was ignored).
It's rc5 now, and this issue still remains. Putting some more lists on
the Cc now.
Reverting the commit still works for me.
With best wishes,
Tobias
----------------
Hello Al,
compiled a kernel on armv7 with torvalds/master today and getting some
errors during the modpost phase.
> ERROR: "_set_bit" [sound/usb/snd-usbmidi-lib.ko] undefined!
> ERROR: "_test_and_set_bit" [sound/usb/snd-usbmidi-lib.ko] undefined!
> ERROR: "_clear_bit" [sound/usb/snd-usbmidi-lib.ko] undefined!
> ERROR: "_test_and_clear_bit" [sound/usb/snd-usb-audio.ko] undefined!
> ERROR: "_set_bit" [sound/usb/snd-usb-audio.ko] undefined!
> ERROR: "_test_and_set_bit" [sound/usb/snd-usb-audio.ko] undefined!
> ERROR: "_clear_bit" [sound/usb/snd-usb-audio.ko] undefined!
> ERROR: "_test_and_clear_bit" [sound/core/seq/snd-seq.ko] undefined!
<snip>
It seems like the commit 'arm: move exports to definitions' introduces
this issue.
I quickly went over the commit and I noticed that while it removes the
EXPORT_SYMBOL()s for the bitops from armksyms.c, it doesn't move them
anywhere.
Maybe you can take a look at this?
With best wishes,
Tobias
^ permalink raw reply
* [arm-soc:qcom/arm64 3/13] Error: arch/arm64/boot/dts/qcom/apq8096-db820c-pmic-pins.dtsi:12.20-21 syntax error
From: Andy Gross @ 2016-11-20 4:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201611181938.YpOhQOad%fengguang.wu@intel.com>
On Fri, Nov 18, 2016 at 07:01:40PM +0800, kbuild test robot wrote:
> Hi Srinivas,
>
> FYI, the error/warning still remains.
Ok, I see what happened. I included the dt-bindings file in the drivers pull
erroneously. This should have gone in the ARM64 DTS pull. As such it will work
when all pulls are together, but not when done as a single unit. My apologies
for mixing this up.
Andy
>
> tree: https://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git qcom/arm64
> head: feeaf56ac78d283efe65ea60ec999d4bf3cf395e
> commit: 50784e61032d89cbbc46ed73a5fb15f27940b947 [3/13] dts: arm64: db820c: add pmic pins specific dts file
> config: arm64-defconfig (attached as .config)
> compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
> reproduce:
> wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> git checkout 50784e61032d89cbbc46ed73a5fb15f27940b947
> # save the attached .config to linux build tree
> make.cross ARCH=arm64
>
> All errors (new ones prefixed by >>):
>
> >> Error: arch/arm64/boot/dts/qcom/apq8096-db820c-pmic-pins.dtsi:12.20-21 syntax error
> FATAL ERROR: Unable to parse input tree
>
> ---
> 0-DAY kernel test infrastructure Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all Intel Corporation
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] arm: spin one more cycle in timer-based delays
From: Afzal Mohammed @ 2016-11-20 6:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161119110301.GQ1041@n2100.armlinux.org.uk>
Hi Russell,
On Sat, Nov 19, 2016 at 11:03:01AM +0000, Russell King - ARM Linux wrote:
> > > 6c5e9059692567740a4ee51530dffe51a4b9584d
> > > https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?h=timers/core&id=6c5e9059692567740a4ee51530dffe51a4b9584d
> >
> > But the above "timers: Fix usleep_range() in the context of
> > wake_up_process()" is to avoid wakeup causing premature return than
> > about being precise, no ?
> usleep*() is based on the scheduler, which has tighter requirements
> laid down in POSIX amongst other standards, such as "not timing out
> before this specified time"
Thanks for educating on the POSIX linkage.
When the above mentioned patch was flying, tglx initially mentioned
that there is no gurantee that usleep_range() will never return in less
time than the specified minimum, but later he committed the change to
tip-bot keeping the message saying otherwise. That was confusing, and
me being a wayfarer, didn't have the courage to ask.
Regards
afzal
^ permalink raw reply
* Low network throughput on i.MX28
From: Jörg Krause @ 2016-11-20 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1442387496.277549.fb1ea129-460b-466b-9575-ed6f40b78b7e.open-xchange@email.1und1.de>
Hi Stefan,
On Sat, 2016-11-19 at 12:36 +0100, Stefan Wahren wrote:
> Hi J?rg,
>
> > J?rg Krause <joerg.krause@embedded.rocks> hat am 19. November 2016
> > um 00:49
> > geschrieben:
> >
> >
> > Hi all,
> >
> > [snip]
> >
> > I did some time measurements on the wifi, mmc and dma driver to
> > compare
> > the performance between the vendor and the mainline kernel. For
> > this I
> > toggled some GPIOs and measured the time difference with an osci. I
> > started measuring the time before calling sdio_readsb() in the wifi
> > driver [1] and stopped the time when the call returns. Note that
> > the
> > time was only measured for a packet length of 1536 bytes.
> >
> > The vendor kernel took about 250 us to return whereas the mainline
> > kernel took about 325 us. To investigate where this additional time
> > comes from I divided the whole procedure into seperate parts and
> > compared their time consumed.
> >
> > I noticed that the mainline kernel does took much longer to return
> > after the DMA request is done, signalled in this case by calling
> > mxs_mmc_dma_irq_callback() [2] in the mxs-mmc driver. From here it
> > takes about 150 us to get back to sdio_readsb().
> >
> > An example for consuming much more time is the mainline mmc driver
> > where it hangs in?mmc_wait_done() [2] about 50 us just calling
> > complete(), whereas the vendor mmc driver almost immediately
> > returns
> > here.
> >
> > I wonder why this call to complete consumes so much time? Any
> > ideas?
>
> i don't know why, but how about putting the SDIO clk signal parallel
> to the
> GPIOs at your osci? So could get a better view of the runtime
> behavior.
Unfortunately, the board layout does not allow me to access the SDIO
pins.
The main question for me is, why the mmc core driver needs around 120
us beginning from calling complete() in mmc_wait_done() [1] until
receiving the completion signal in mmc_wait_for_req_done() [2]. Why
does signaling the completion consumes so much time?
For comparision, the time to do the mmc request (preparing request,
preparing DMA, doing DMA, waiting, reading response, starting signal
completion) takes about 215 us, whereas just sending the signal that
completion is done takes 120 us. For me this issue is the bottleneck.
Does anyone has an idea what may be responsible that signaling the
completion is so slow?
[1] http://lxr.free-electrons.com/source/drivers/mmc/core/core.c#L386
[2] http://lxr.free-electrons.com/source/drivers/mmc/core/core.c#L492
> Btw you should also verify the necessary time between to 2 packets.
>
> Stefan
>
> >
> > [1] http://lxr.free-electrons.com/source/drivers/net/wireless/broad
> > com/
> > brcm80211/brcmfmac/bcmsdh.c#L488
> >
> > [2] http://lxr.free-electrons.com/source/drivers/mmc/host/mxs-mmc.c
> > #L17
> > 9
> >
> > [3] http://lxr.free-electrons.com/source/drivers/mmc/core/core.c#L3
> > 86
> >
> > Best regards,
> > J?rg Krause
^ permalink raw reply
* [PATCH v6 1/5] drm: sun8i: Add a basic DRM driver for Allwinner DE2
From: Jean-Francois Moine @ 2016-11-20 9:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1479641523.git.moinejf@free.fr>
Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
engine, DE2.
This patch adds a DRM video driver for this device.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
.../bindings/display/sunxi/sun8i-de2.txt | 83 +++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/sun8i/Kconfig | 19 +
drivers/gpu/drm/sun8i/Makefile | 7 +
drivers/gpu/drm/sun8i/de2_crtc.c | 440 +++++++++++++
drivers/gpu/drm/sun8i/de2_crtc.h | 50 ++
drivers/gpu/drm/sun8i/de2_drm.h | 48 ++
drivers/gpu/drm/sun8i/de2_drv.c | 379 +++++++++++
drivers/gpu/drm/sun8i/de2_plane.c | 712 +++++++++++++++++++++
10 files changed, 1741 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
create mode 100644 drivers/gpu/drm/sun8i/Kconfig
create mode 100644 drivers/gpu/drm/sun8i/Makefile
create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
create mode 100644 drivers/gpu/drm/sun8i/de2_drm.h
create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
new file mode 100644
index 0000000..b9edd4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/sun8i-de2.txt
@@ -0,0 +1,83 @@
+Allwinner sun8i Display Engine 2 subsystem
+==========================================
+
+The Allwinner DE2 subsystem contains a display controller (DE2),
+one or two LCD controllers (TCON) and their external interfaces.
+
+Display controller
+==================
+
+Required properties:
+
+- compatible: value should be one of the following
+ "allwinner,sun8i-a83t-display-engine"
+ "allwinner,sun8i-h3-display-engine"
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: must contain
+ "gate": DE bus gate
+ "clock": DE clock
+
+- resets: phandle to the reset of the device
+
+- ports: phandle's to the LCD ports
+
+LCD controller
+==============
+
+Required properties:
+
+- compatible: should be
+ "allwinner,sun8i-a83t-tcon"
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: must contain
+ "gate": TCON bus gate
+ "clock": TCON pixel clock
+
+- resets: phandle to the reset of the device
+
+- port: port node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+
+ de: de-controller at 01000000 {
+ compatible = "allwinner,sun8i-h3-display-engine";
+ ...
+ clocks = <&&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
+ clock-names = "gate", "clock";
+ resets = <&ccu RST_BUS_DE>;
+ ports = <&lcd0_p>;
+ };
+
+ lcd0: lcd-controller at 01c0c000 {
+ compatible = "allwinner,sun8i-a83t-tcon";
+ ...
+ clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
+ clock-names = "gate", "clock";
+ resets = <&ccu RST_BUS_TCON0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ lcd0_p: port {
+ lcd0_ep: endpoint {
+ remote-endpoint = <&hdmi_ep>;
+ };
+ };
+ };
+
+ hdmi: hdmi at 01ee0000 {
+ ...
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port {
+ hdmi_ep: endpoint {
+ remote-endpoint = <&lcd0_ep>;
+ };
+ };
+ };
+
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 95fc041..bb1bfbc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
source "drivers/gpu/drm/sun4i/Kconfig"
+source "drivers/gpu/drm/sun8i/Kconfig"
+
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 883f3e7..3e1eaa0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-y += omapdrm/
obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_SUN8I) += sun8i/
obj-y += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
new file mode 100644
index 0000000..6940895
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -0,0 +1,19 @@
+#
+# Allwinner DE2 Video configuration
+#
+
+config DRM_SUN8I
+ bool
+
+config DRM_SUN8I_DE2
+ tristate "Support for Allwinner Video with DE2 interface"
+ depends on DRM && OF
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_SUN8I
+ help
+ Choose this option if your Allwinner chipset has the DE2 interface
+ as the A64, A83T and H3. If M is selected the module will be called
+ sun8i-de2-drm.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
new file mode 100644
index 0000000..f107919
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Allwinner's sun8i DRM device driver
+#
+
+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+
+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
new file mode 100644
index 0000000..65c9b93
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.c
@@ -0,0 +1,440 @@
+/*
+ * Allwinner DRM driver - DE2 CRTC
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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/component.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+
+#include "de2_drm.h"
+#include "de2_crtc.h"
+
+/* I/O map */
+
+#define TCON_GCTL_REG 0x00
+#define TCON_GCTL_TCON_ENABLE BIT(31)
+#define TCON_GINT0_REG 0x04
+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
+#define TCON0_CTL_REG 0x40
+#define TCON0_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_REG 0x90
+#define TCON1_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_INTERLACE_ENABLE BIT(20)
+#define TCON1_CTL_Start_Delay_SHIFT 4
+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
+#define TCON1_BASIC0_REG 0x94 /* XI/YI */
+#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */
+#define TCON1_BASIC2_REG 0x9c /* XO/YO */
+#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */
+#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */
+#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */
+#define TCON1_PS_SYNC_REG 0xb0
+#define TCON1_IO_POL_REG 0xf0
+#define TCON1_IO_POL_IO0_inv BIT(24)
+#define TCON1_IO_POL_IO1_inv BIT(25)
+#define TCON1_IO_POL_IO2_inv BIT(26)
+#define TCON1_IO_TRI_REG 0xf4
+#define TCON_CEU_CTL_REG 0x100
+#define TCON_CEU_CTL_ceu_en BIT(31)
+#define TCON1_FILL_CTL_REG 0x300
+#define TCON1_FILL_START0_REG 0x304
+#define TCON1_FILL_END0_REG 0x308
+#define TCON1_FILL_DATA0_REG 0x30c
+
+#define XY(x, y) (((x) << 16) | (y))
+
+#define andl_relaxed(addr, val) \
+ writel_relaxed(readl_relaxed(addr) & val, addr)
+#define orl_relaxed(addr, val) \
+ writel_relaxed(readl_relaxed(addr) | val, addr)
+
+/* vertical blank functions */
+
+static void de2_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (event) {
+ crtc->state->event = NULL;
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+static irqreturn_t de2_lcd_irq(int irq, void *dev_id)
+{
+ struct lcd *lcd = (struct lcd *) dev_id;
+ u32 isr;
+
+ isr = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
+
+ drm_crtc_handle_vblank(&lcd->crtc);
+
+ writel_relaxed(isr & ~TCON_GINT0_TCON1_Vb_Int_Flag,
+ lcd->mmio + TCON_GINT0_REG);
+
+ return IRQ_HANDLED;
+}
+
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = priv->lcds[crtc_ix];
+
+ orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
+
+ return 0;
+}
+
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = priv->lcds[crtc_ix];
+
+ andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+
+void de2_vblank_reset(struct lcd *lcd)
+{
+ drm_crtc_vblank_reset(&lcd->crtc);
+}
+
+/* frame functions */
+static void de2_tcon_init(struct lcd *lcd)
+{
+ andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+
+ /* disable/ack interrupts */
+ writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
+}
+
+static void de2_tcon_enable(struct lcd *lcd)
+{
+ struct drm_crtc *crtc = &lcd->crtc;
+ const struct drm_display_mode *mode = &crtc->mode;
+ int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
+ int start_delay;
+ u32 data;
+
+ orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
+
+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
+ writel_relaxed(XY(mode->htotal - 1,
+ mode->htotal - mode->hsync_start - 1),
+ lcd->mmio + TCON1_BASIC3_REG);
+ writel_relaxed(XY(mode->vtotal * (3 - interlace),
+ mode->vtotal - mode->vsync_start - 1),
+ lcd->mmio + TCON1_BASIC4_REG);
+ writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
+ mode->vsync_end - mode->vsync_start - 1),
+ lcd->mmio + TCON1_BASIC5_REG);
+
+ writel_relaxed(XY(1, 1), lcd->mmio + TCON1_PS_SYNC_REG);
+
+ data = TCON1_IO_POL_IO2_inv;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ data |= TCON1_IO_POL_IO0_inv;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ data |= TCON1_IO_POL_IO1_inv;
+ writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
+
+ andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
+
+ if (interlace == 2)
+ orl_relaxed(lcd->mmio + TCON1_CTL_REG,
+ TCON1_CTL_INTERLACE_ENABLE);
+ else
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG,
+ ~TCON1_CTL_INTERLACE_ENABLE);
+
+ writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
+ writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
+ writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
+ writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
+
+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
+ if (start_delay > 31)
+ start_delay = 31;
+ data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
+ data &= ~TCON1_CTL_Start_Delay_MASK;
+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
+ writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
+
+ writel_relaxed(0x0fffffff, /* TRI disabled */
+ lcd->mmio + TCON1_IO_TRI_REG);
+
+ orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
+}
+
+static void de2_tcon_disable(struct lcd *lcd)
+{
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+}
+
+static void de2_crtc_enable(struct drm_crtc *crtc)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct drm_display_mode *mode = &crtc->mode;
+ struct clk *parent_clk;
+ u32 parent_rate;
+ int ret;
+
+ /* determine and set the best rate for the parent clock (pll-video) */
+ if (297000 % mode->clock == 0)
+ parent_rate = 297000000;
+ else if ((270000 * 2) % mode->clock == 0)
+ parent_rate = 270000000;
+ else
+ return; /* "640x480" rejected */
+ parent_clk = clk_get_parent(lcd->clk);
+
+ ret = clk_set_rate(parent_clk, parent_rate);
+ if (ret) {
+ dev_err(lcd->dev, "set parent rate failed %d\n", ret);
+ return;
+ }
+
+ /* then, set the TCON clock rate */
+ ret = clk_set_rate(lcd->clk, mode->clock * 1000);
+ if (ret) {
+ dev_err(lcd->dev, "set rate %dKHz failed %d\n",
+ mode->clock, ret);
+ return;
+ }
+
+ /* start the TCON */
+ reset_control_deassert(lcd->reset);
+ clk_prepare_enable(lcd->bus);
+ clk_prepare_enable(lcd->clk);
+ lcd->clk_enabled = true;
+
+ de2_tcon_enable(lcd);
+
+ de2_de_enable(lcd);
+
+ /* turn on blanking interrupt */
+ drm_crtc_vblank_on(crtc);
+}
+
+static void de2_crtc_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+
+ if (!lcd->clk_enabled)
+ return; /* already disabled */
+ lcd->clk_enabled = false;
+
+ de2_de_disable(lcd);
+
+ drm_crtc_vblank_off(crtc);
+
+ de2_tcon_disable(lcd);
+
+ clk_disable_unprepare(lcd->clk);
+ clk_disable_unprepare(lcd->bus);
+ reset_control_assert(lcd->reset);
+}
+
+static const struct drm_crtc_funcs de2_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {
+ .atomic_flush = de2_atomic_flush,
+ .enable = de2_crtc_enable,
+ .atomic_disable = de2_crtc_disable,
+};
+
+/* device init */
+static int de2_lcd_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = data;
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = dev_get_drvdata(dev);
+ struct drm_crtc *crtc = &lcd->crtc;
+ int ret, index;
+
+ lcd->priv = priv;
+
+ ret = de2_plane_init(drm, lcd);
+ if (ret < 0)
+ return ret;
+
+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, crtc,
+ &lcd->planes[DE2_PRIMARY_PLANE],
+ &lcd->planes[DE2_CURSOR_PLANE],
+ &de2_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ /* set the lcd/crtc reference */
+ index = drm_crtc_index(crtc);
+ if (index >= ARRAY_SIZE(priv->lcds)) {
+ dev_err(drm->dev, "Bad crtc index");
+ return -ENOENT;
+ }
+ priv->lcds[index] = lcd;
+
+ return ret;
+}
+
+static void de2_lcd_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ if (lcd->priv)
+ lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
+}
+
+static const struct component_ops de2_lcd_ops = {
+ .bind = de2_lcd_bind,
+ .unbind = de2_lcd_unbind,
+};
+
+static int de2_lcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node, *tmp, *parent, *port;
+ struct lcd *lcd;
+ struct resource *res;
+ int id, irq, ret;
+
+ lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ /* get the LCD (mixer) number */
+ id = of_alias_get_id(np, "lcd");
+ if (id < 0 || id >= 2) {
+ dev_err(dev, "no or bad alias for lcd\n");
+ id = 0;
+ }
+ dev_set_drvdata(dev, lcd);
+ lcd->dev = dev;
+ lcd->mixer = id;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -EINVAL;
+ }
+
+ lcd->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lcd->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(lcd->mmio);
+ }
+
+ /* possible CRTCs */
+ parent = np;
+ tmp = of_get_child_by_name(np, "ports");
+ if (tmp)
+ parent = tmp;
+ port = of_get_child_by_name(parent, "port");
+ of_node_put(tmp);
+ if (!port) {
+ dev_err(dev, "no port node\n");
+ return -ENXIO;
+ }
+ lcd->crtc.port = port;
+
+ lcd->bus = devm_clk_get(dev, "bus");
+ if (IS_ERR(lcd->bus)) {
+ dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
+ ret = PTR_ERR(lcd->bus);
+ goto err;
+ }
+
+ lcd->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(lcd->clk)) {
+ ret = PTR_ERR(lcd->clk);
+ dev_err(dev, "get video clock err %d\n", ret);
+ goto err;
+ }
+
+ lcd->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(lcd->reset)) {
+ ret = PTR_ERR(lcd->reset);
+ dev_err(dev, "get reset err %d\n", ret);
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "unable to get irq\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ de2_tcon_init(lcd); /* stop TCON and avoid interrupts */
+
+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
+ dev_name(dev), lcd);
+ if (ret < 0) {
+ dev_err(dev, "unable to request irq %d\n", irq);
+ goto err;
+ }
+
+ return component_add(dev, &de2_lcd_ops);
+
+err:
+ of_node_put(lcd->crtc.port);
+ return ret;
+}
+
+static int de2_lcd_remove(struct platform_device *pdev)
+{
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ component_del(&pdev->dev, &de2_lcd_ops);
+
+ of_node_put(lcd->crtc.port);
+
+ return 0;
+}
+
+static const struct of_device_id de2_lcd_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-tcon", },
+ { }
+};
+
+struct platform_driver de2_lcd_platform_driver = {
+ .probe = de2_lcd_probe,
+ .remove = de2_lcd_remove,
+ .driver = {
+ .name = "sun8i-de2-tcon",
+ .of_match_table = of_match_ptr(de2_lcd_ids),
+ },
+};
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
new file mode 100644
index 0000000..f663ba4
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.h
@@ -0,0 +1,50 @@
+#ifndef __DE2_CRTC_H__
+#define __DE2_CRTC_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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 <drm/drm_plane_helper.h>
+
+struct clk;
+struct reset_control;
+struct priv;
+
+/* planes */
+#define DE2_PRIMARY_PLANE 0
+#define DE2_CURSOR_PLANE 1
+#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */
+
+struct lcd {
+ void __iomem *mmio;
+
+ struct device *dev;
+ struct drm_crtc crtc;
+
+ struct priv *priv; /* DRM/DE private data */
+
+ u8 mixer; /* LCD (mixer) number */
+ u8 delayed; /* bitmap of planes with delayed update */
+
+ u8 clk_enabled; /* used for error in crtc_enable */
+
+ struct clk *clk;
+ struct clk *bus;
+ struct reset_control *reset;
+
+ struct drm_plane planes[DE2_N_PLANES];
+};
+
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+
+/* in de2_plane.c */
+void de2_de_enable(struct lcd *lcd);
+void de2_de_disable(struct lcd *lcd);
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+
+#endif /* __DE2_CRTC_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_drm.h b/drivers/gpu/drm/sun8i/de2_drm.h
new file mode 100644
index 0000000..c42c30a
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drm.h
@@ -0,0 +1,48 @@
+#ifndef __DE2_DRM_H__
+#define __DE2_DRM_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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 <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+struct drm_fbdev_cma;
+struct lcd;
+
+#define N_LCDS 2
+
+struct priv {
+ struct drm_device drm;
+ void __iomem *mmio;
+ struct clk *clk;
+ struct clk *gate;
+ struct reset_control *reset;
+
+ struct mutex mutex; /* protect DE I/O access */
+ u8 soc_type;
+#define SOC_A83T 0
+#define SOC_H3 1
+ u8 started; /* bitmap of started mixers */
+ u8 clean; /* bitmap of clean mixers */
+
+ struct drm_fbdev_cma *fbdev;
+
+ struct lcd *lcds[N_LCDS]; /* CRTCs */
+};
+
+#define drm_to_priv(x) container_of(x, struct priv, drm)
+
+/* in de2_crtc.c */
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_vblank_reset(struct lcd *lcd);
+extern struct platform_driver de2_lcd_platform_driver;
+
+#endif /* __DE2_DRM_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
new file mode 100644
index 0000000..67368f5
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.c
@@ -0,0 +1,379 @@
+/*
+ * Allwinner DRM driver - DE2 DRM driver
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drm.h"
+
+#define DRIVER_NAME "sun8i-de2"
+#define DRIVER_DESC "Allwinner DRM DE2"
+#define DRIVER_DATE "20161101"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static const struct of_device_id de2_drm_of_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-display-engine",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-display-engine",
+ .data = (void *) SOC_H3 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, de2_drm_of_match);
+
+static void de2_fb_output_poll_changed(struct drm_device *drm)
+{
+ struct priv *priv = drm_to_priv(drm);
+
+ if (priv->fbdev)
+ drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+
+static const struct drm_mode_config_funcs de2_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = de2_fb_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+/* -- DRM operations -- */
+
+static void de2_lastclose(struct drm_device *drm)
+{
+ struct priv *priv = drm_to_priv(drm);
+
+ if (priv->fbdev)
+ drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static const struct file_operations de2_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver de2_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+ DRIVER_ATOMIC,
+ .lastclose = de2_lastclose,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .enable_vblank = de2_enable_vblank,
+ .disable_vblank = de2_disable_vblank,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .fops = &de2_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+/*
+ * Platform driver
+ */
+
+static int de2_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ struct priv *priv;
+ struct resource *res;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ drm = &priv->drm;
+ dev_set_drvdata(dev, drm);
+
+ /* get the resources */
+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
+
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ ret = -EINVAL;
+ goto out1;
+ }
+
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ dev_err(dev, "failed to map registers %d\n", ret);
+ goto out1;
+ }
+
+ priv->gate = devm_clk_get(dev, "bus");
+ if (IS_ERR(priv->gate)) {
+ ret = PTR_ERR(priv->gate);
+ dev_err(dev, "bus gate err %d\n", ret);
+ goto out1;
+ }
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "clock err %d\n", ret);
+ goto out1;
+ }
+
+ priv->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "reset err %d\n", ret);
+ goto out1;
+ }
+
+ mutex_init(&priv->mutex); /* protect DE I/O accesses */
+
+ ret = drm_dev_init(drm, &de2_drm_driver, dev);
+ if (ret != 0) {
+ dev_err(dev, "out of memory\n");
+ goto out1;
+ }
+
+ drm_mode_config_init(drm);
+ drm->mode_config.min_width = 32; /* needed for cursor */
+ drm->mode_config.min_height = 32;
+ drm->mode_config.max_width = 1920;
+ drm->mode_config.max_height = 1080;
+ drm->mode_config.funcs = &de2_mode_config_funcs;
+
+ drm->irq_enabled = true;
+
+ /* start the subdevices */
+ ret = component_bind_all(dev, drm);
+ if (ret < 0)
+ goto out2;
+
+ /* initialize and disable vertical blanking on all CRTCs */
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret < 0)
+ dev_warn(dev, "failed to initialize vblank\n");
+
+ {
+ struct lcd *lcd;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
+ lcd = priv->lcds[i];
+ if (lcd)
+ de2_vblank_reset(lcd);
+ }
+ }
+
+ drm_mode_config_reset(drm);
+
+ priv->fbdev = drm_fbdev_cma_init(drm,
+ 32, /* bpp */
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+ if (IS_ERR(priv->fbdev)) {
+ ret = PTR_ERR(priv->fbdev);
+ priv->fbdev = NULL;
+ goto out3;
+ }
+
+ drm_kms_helper_poll_init(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto out4;
+
+ return 0;
+
+out4:
+ drm_fbdev_cma_fini(priv->fbdev);
+out3:
+ component_unbind_all(dev, drm);
+out2:
+ drm_dev_unref(drm);
+out1:
+ kfree(priv);
+ return ret;
+}
+
+static void de2_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct priv *priv = drm_to_priv(drm);
+
+ drm_dev_unregister(drm);
+
+ drm_fbdev_cma_fini(priv->fbdev);
+ drm_kms_helper_poll_fini(drm);
+ drm_vblank_cleanup(drm);
+ drm_mode_config_cleanup(drm);
+
+ component_unbind_all(dev, drm);
+
+ kfree(priv);
+}
+
+static const struct component_master_ops de2_drm_comp_ops = {
+ .bind = de2_drm_bind,
+ .unbind = de2_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int de2_drm_add_components(struct device *dev,
+ int (*compare_of)(struct device *, void *),
+ const struct component_master_ops *m_ops)
+{
+ struct device_node *ep, *port, *remote;
+ struct component_match *match = NULL;
+ int i;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ /* bind the CRTCs */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(dev->of_node, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ component_match_add(dev, &match, compare_of, port->parent);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+ if (!match) {
+ dev_err(dev, "no available port\n");
+ return -ENODEV;
+ }
+
+ /* bind the encoders/connectors */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(dev->of_node, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ }
+ if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev,
+ "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, &match, compare_of, remote);
+ of_node_put(remote);
+ }
+ of_node_put(port);
+ }
+
+ return component_master_add_with_match(dev, m_ops, match);
+}
+
+static int de2_drm_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = de2_drm_add_components(&pdev->dev,
+ compare_of,
+ &de2_drm_comp_ops);
+ if (ret == -EINVAL)
+ ret = -ENXIO;
+ return ret;
+}
+
+static int de2_drm_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &de2_drm_comp_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_drm_platform_driver = {
+ .probe = de2_drm_probe,
+ .remove = de2_drm_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = de2_drm_of_match,
+ },
+};
+
+static int __init de2_drm_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&de2_lcd_platform_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = platform_driver_register(&de2_drm_platform_driver);
+ if (ret < 0)
+ platform_driver_unregister(&de2_lcd_platform_driver);
+
+ return ret;
+}
+
+static void __exit de2_drm_fini(void)
+{
+ platform_driver_unregister(&de2_lcd_platform_driver);
+ platform_driver_unregister(&de2_drm_platform_driver);
+}
+
+module_init(de2_drm_init);
+module_exit(de2_drm_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
new file mode 100644
index 0000000..47c94dd
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_plane.c
@@ -0,0 +1,712 @@
+/*
+ * Allwinner DRM driver - Display Engine 2
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * 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/io.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "de2_drm.h"
+#include "de2_crtc.h"
+
+/* DE2 I/O map */
+
+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */
+#define DE2_GATE_REG 0x0004
+#define DE2_RESET_REG 0x0008
+#define DE2_DIV_REG 0x000c /* 4 bits per LCD */
+#define DE2_SEL_REG 0x0010
+
+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */
+#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */
+
+/* mixer registers (addr / mixer base) */
+#define MIXER_GLB_REGS 0x00000 /* global control */
+#define MIXER_BLD_REGS 0x01000 /* alpha blending */
+#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */
+#define MIXER_CHAN_SZ 0x1000 /* size of a channel */
+#define MIXER_VSU_REGS 0x20000 /* VSU */
+#define MIXER_GSU1_REGS 0x30000 /* GSUs */
+#define MIXER_GSU2_REGS 0x40000
+#define MIXER_GSU3_REGS 0x50000
+#define MIXER_FCE_REGS 0xa0000 /* FCE */
+#define MIXER_BWS_REGS 0xa2000 /* BWS */
+#define MIXER_LTI_REGS 0xa4000 /* LTI */
+#define MIXER_PEAK_REGS 0xa6000 /* PEAK */
+#define MIXER_ASE_REGS 0xa8000 /* ASE */
+#define MIXER_FCC_REGS 0xaa000 /* FCC */
+#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */
+
+/* global control */
+#define MIXER_GLB_CTL_REG 0x00
+#define MIXER_GLB_CTL_rt_en BIT(0)
+#define MIXER_GLB_CTL_finish_irq_en BIT(4)
+#define MIXER_GLB_CTL_rtwb_port BIT(12)
+#define MIXER_GLB_STATUS_REG 0x04
+#define MIXER_GLB_DBUFF_REG 0x08
+#define MIXER_GLB_SIZE_REG 0x0c
+
+/* alpha blending */
+#define MIXER_BLD_FCOLOR_CTL_REG 0x00
+#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))
+#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */
+#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */
+#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ROUTE_REG 0x80
+#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
+#define MIXER_BLD_PREMULTIPLY_REG 0x84
+#define MIXER_BLD_BKCOLOR_REG 0x88
+#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c
+#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */
+#define MIXER_BLD_MODE_SRCOVER 0x03010301
+#define MIXER_BLD_OUT_CTL_REG 0xfc
+
+/* VI channel (channel 0) */
+#define VI_CFG_N 4 /* number of layers */
+#define VI_CFG_SIZE 0x30 /* size of a layer */
+#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))
+#define VI_CFG_ATTR_en BIT(0)
+#define VI_CFG_ATTR_fcolor_en BIT(4)
+#define VI_CFG_ATTR_fmt_SHIFT 8
+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define VI_CFG_ATTR_ui_sel BIT(15)
+#define VI_CFG_ATTR_top_down BIT(23)
+#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))
+#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))
+#define VI_N_PLANES 3
+#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_FCOLORx(l) (0xc0 + 4 * (l))
+#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))
+#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))
+#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))
+#define VI_HORI_DSx(n) (0xf0 + 4 * (n))
+#define VI_VERT_DSx(n) (0xf8 + 4 * (n))
+#define VI_SIZE 0x100
+
+/* UI channel (channels 1..3) */
+#define UI_CFG_N 4 /* number of layers */
+#define UI_CFG_SIZE (8 * 4) /* size of a layer */
+#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))
+#define UI_CFG_ATTR_en BIT(0)
+#define UI_CFG_ATTR_alpmod_SHIFT 1
+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
+#define UI_CFG_ATTR_fcolor_en BIT(4)
+#define UI_CFG_ATTR_fmt_SHIFT 8
+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define UI_CFG_ATTR_top_down BIT(23)
+#define UI_CFG_ATTR_alpha_SHIFT 24
+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
+#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))
+#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))
+#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))
+#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))
+#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))
+#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))
+#define UI_TOP_HADDR 0x80
+#define UI_BOT_HADDR 0x84
+#define UI_OVL_SIZE 0x88
+#define UI_SIZE 0x8c
+
+/* coordinates and sizes */
+#define XY(x, y) (((y) << 16) | (x))
+#define WH(w, h) ((((h) - 1) << 16) | ((w) - 1))
+
+/* UI video formats */
+#define DE2_FORMAT_ARGB_8888 0
+#define DE2_FORMAT_BGRA_8888 3
+#define DE2_FORMAT_XRGB_8888 4
+#define DE2_FORMAT_RGB_888 8
+#define DE2_FORMAT_BGR_888 9
+
+/* VI video formats */
+#define DE2_FORMAT_YUV422_I_YVYU 1 /* YVYU */
+#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */
+#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */
+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
+
+/* plane formats */
+static const uint32_t ui_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+static const uint32_t vi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_YUV422,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+/*
+ * plane table
+ *
+ * The chosen channel/layer assignment of the planes respects
+ * the following constraints:
+ * - the cursor must be in a channel higher than the primary channel
+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
+ */
+static const struct {
+ u8 chan;
+ u8 layer;
+ u8 pipe;
+ u8 type; /* plane type */
+ const uint32_t *formats;
+ u8 n_formats;
+} plane_tb[] = {
+ [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */
+ 0, 0, 0,
+ DRM_PLANE_TYPE_PRIMARY,
+ ui_formats, ARRAY_SIZE(ui_formats),
+ },
+ [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */
+ 1, 0, 1,
+ DRM_PLANE_TYPE_CURSOR,
+ ui_formats, ARRAY_SIZE(ui_formats),
+ },
+ {
+ 0, 1, 0, /* 1st overlay: channel 0, layer 1 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+ {
+ 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+ {
+ 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+};
+
+static inline void andl_relaxed(void __iomem *addr, u32 val)
+{
+ writel_relaxed(readl_relaxed(addr) & val, addr);
+}
+
+static inline void orl_relaxed(void __iomem *addr, u32 val)
+{
+ writel_relaxed(readl_relaxed(addr) | val, addr);
+}
+
+/* alert the DE processor about changes in a mixer configuration */
+static void de2_mixer_select(struct priv *priv,
+ int mixer,
+ void __iomem *mixer_io)
+{
+ /* select the mixer ? */
+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+
+ /* double register switch */
+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+}
+
+/*
+ * cleanup a mixer
+ *
+ * This is needed only once after power on.
+ */
+static void de2_mixer_cleanup(struct priv *priv, int mixer)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ u32 size = WH(1920, 1080); /* (any size) */
+ unsigned int i;
+ u32 data;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+ chan_io = mixer_io + MIXER_CHAN_REGS;
+
+ /* set the A83T clock divider (500 / 2) = 250MHz */
+ if (priv->soc_type == SOC_A83T)
+ writel_relaxed(0x00000011, /* div = 2 for both LCDs */
+ priv->mmio + DE2_DIV_REG);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+ /*
+ * clear the VI/UI channels
+ * LCD0: 1 VI and 3 UIs
+ * LCD1: 1 VI and 1 UI
+ */
+ memset_io(chan_io, 0, VI_SIZE);
+ memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
+ if (mixer == 0) {
+ memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
+ memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
+ }
+
+ /* clear and set default values alpha blending */
+ memset_io(mixer_io + MIXER_BLD_REGS, 0,
+ MIXER_BLD_ATTR_SIZE * MIXER_BLD_ATTR_N);
+ writel_relaxed(0x00000001 | /* fcolor for primary */
+ MIXER_BLD_FCOLOR_CTL_PEN(0),
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+ for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
+ writel_relaxed(0xff000000,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
+ writel_relaxed(size,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
+ writel_relaxed(0,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
+ }
+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+
+ /* prepare the pipe route for the planes */
+ data = 0;
+ for (i = 0; i < DE2_N_PLANES; i++)
+ data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
+ writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
+
+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_PREMULTIPLY_REG);
+ writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_BKCOLOR_REG);
+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_OUTPUT_SIZE_REG);
+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+
+ /* disable the enhancements */
+ writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
+ writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
+ writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
+ writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
+ writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
+ writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
+ writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
+ writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
+}
+
+/* enable a mixer */
+static void de2_mixer_enable(struct lcd *lcd)
+{
+ struct priv *priv = lcd->priv;
+ void __iomem *mixer_io = priv->mmio;
+ int mixer = lcd->mixer;
+ u32 data;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ if (priv->started & (1 << mixer))
+ return; /* mixer already enabled */
+
+ /* if not done yet, start the DE processor */
+ if (!priv->started) {
+ reset_control_deassert(priv->reset);
+ clk_prepare_enable(priv->gate);
+ clk_prepare_enable(priv->clk);
+ }
+ priv->started |= 1 << mixer;
+
+ /* deassert the mixer and enable the clock */
+ orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
+ data = 1 << mixer; /* 1 bit / lcd */
+ orl_relaxed(priv->mmio + DE2_GATE_REG, data);
+ orl_relaxed(priv->mmio + DE2_MOD_REG, data);
+
+ /* enable */
+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1); /* mixer select */
+ writel_relaxed(MIXER_GLB_CTL_rt_en | MIXER_GLB_CTL_rtwb_port,
+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+ /* restore the frame buffer size */
+ writel_relaxed(WH(lcd->crtc.mode.hdisplay,
+ lcd->crtc.mode.vdisplay),
+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+ /* if not yet done, cleanup */
+ if (!(priv->clean & (1 << mixer))) {
+ priv->clean |= 1 << mixer;
+ de2_mixer_cleanup(priv, mixer);
+ }
+}
+
+/* enable a LCD (DE mixer) */
+void de2_de_enable(struct lcd *lcd)
+{
+ mutex_lock(&lcd->priv->mutex);
+
+ de2_mixer_enable(lcd);
+
+ mutex_unlock(&lcd->priv->mutex);
+}
+
+/* disable a LCD (DE mixer) */
+void de2_de_disable(struct lcd *lcd)
+{
+ struct priv *priv = lcd->priv;
+ void __iomem *mixer_io = priv->mmio;
+ int mixer = lcd->mixer;
+ u32 data;
+
+ if (!(priv->started & (1 << mixer)))
+ return; /* mixer already disabled */
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ mutex_lock(&priv->mutex);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+
+ data = ~(1 << mixer);
+ andl_relaxed(priv->mmio + DE2_MOD_REG, data);
+ andl_relaxed(priv->mmio + DE2_GATE_REG, data);
+ andl_relaxed(priv->mmio + DE2_RESET_REG, data);
+
+ mutex_unlock(&priv->mutex);
+
+ /* if all mixers are disabled, stop the DE */
+ priv->started &= ~(1 << mixer);
+ if (!priv->started) {
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ reset_control_assert(priv->reset);
+ }
+}
+
+static void de2_vi_update(void __iomem *chan_io,
+ struct drm_gem_cma_object *gem,
+ int layer,
+ unsigned int fmt,
+ u32 ui_sel,
+ u32 size,
+ u32 coord,
+ struct drm_framebuffer *fb,
+ u32 screen_size)
+{
+ int i;
+
+ writel_relaxed(VI_CFG_ATTR_en |
+ (fmt << VI_CFG_ATTR_fmt_SHIFT) |
+ ui_sel,
+ chan_io + VI_CFGx_ATTR(layer));
+ writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+ for (i = 0; i < VI_N_PLANES; i++) {
+ writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
+ fb->pitches[0],
+ chan_io + VI_CFGx_PITCHy(layer, i));
+ writel_relaxed(gem->paddr + fb->offsets[i],
+ chan_io + VI_CFGx_TOP_LADDRy(layer, i));
+ }
+ writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
+ if (layer == 0) {
+ writel_relaxed(screen_size,
+ chan_io + VI_OVL_SIZEx(0));
+ }
+}
+
+static void de2_ui_update(void __iomem *chan_io,
+ struct drm_gem_cma_object *gem,
+ int layer,
+ unsigned int fmt,
+ u32 alpha_glob,
+ u32 size,
+ u32 coord,
+ struct drm_framebuffer *fb,
+ u32 screen_size)
+{
+ writel_relaxed(UI_CFG_ATTR_en |
+ (fmt << UI_CFG_ATTR_fmt_SHIFT) |
+ alpha_glob,
+ chan_io + UI_CFGx_ATTR(layer));
+ writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+ writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
+ writel_relaxed(gem->paddr + fb->offsets[0],
+ chan_io + UI_CFGx_TOP_LADDR(layer));
+ if (layer == 0)
+ writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
+}
+
+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
+ int plane_num,
+ struct drm_plane_state *state,
+ struct drm_plane_state *old_state)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_gem_cma_object *gem;
+ u32 size = WH(state->crtc_w, state->crtc_h);
+ u32 coord, screen_size;
+ u32 fcolor;
+ u32 ui_sel, alpha_glob;
+ int mixer = lcd->mixer;
+ int chan, layer, x, y;
+ unsigned int fmt;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ chan = plane_tb[plane_num].chan;
+ layer = plane_tb[plane_num].layer;
+
+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+ x = state->crtc_x >= 0 ? state->crtc_x : 0;
+ y = state->crtc_y >= 0 ? state->crtc_y : 0;
+ coord = XY(x, y);
+
+ /* if plane update was delayed, force a full update */
+ if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
+ (1 << plane_num)) {
+ priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
+ ~(1 << plane_num);
+
+ /* handle plane move */
+ } else if (fb == old_state->fb) {
+ de2_mixer_select(priv, mixer, mixer_io);
+ if (chan == 0)
+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+ else
+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+ return;
+ }
+
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+ ui_sel = alpha_glob = 0;
+
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_ARGB8888:
+ fmt = DE2_FORMAT_ARGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ fmt = DE2_FORMAT_BGRA_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ fmt = DE2_FORMAT_XRGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
+ (0xff << UI_CFG_ATTR_alpha_SHIFT);
+ break;
+ case DRM_FORMAT_RGB888:
+ fmt = DE2_FORMAT_RGB_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGR888:
+ fmt = DE2_FORMAT_BGR_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_YUYV:
+ fmt = DE2_FORMAT_YUV422_I_YUYV;
+ break;
+ case DRM_FORMAT_YVYU:
+ fmt = DE2_FORMAT_YUV422_I_YVYU;
+ break;
+ case DRM_FORMAT_YUV422:
+ fmt = DE2_FORMAT_YUV422_P;
+ break;
+ case DRM_FORMAT_YUV420:
+ fmt = DE2_FORMAT_YUV420_P;
+ break;
+ case DRM_FORMAT_UYVY:
+ fmt = DE2_FORMAT_YUV422_I_UYVY;
+ break;
+ default:
+ pr_err("de2_plane_update: format %.4s not yet treated\n",
+ (char *) &fb->pixel_format);
+ return;
+ }
+
+ /* the overlay size is the one of the primary plane */
+ screen_size = plane_num == DE2_PRIMARY_PLANE ?
+ size :
+ readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+ /* prepare pipe enable */
+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+ fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ if (chan == 0) /* VI channel */
+ de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
+ fb, screen_size);
+ else /* UI channel */
+ de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
+ fb, screen_size);
+ writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static void de2_plane_disable(struct priv *priv,
+ int mixer, int plane_num)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ u32 fcolor;
+ int chan, layer, chan_disable = 0;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ chan = plane_tb[plane_num].chan;
+ layer = plane_tb[plane_num].layer;
+
+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+ /*
+ * check if the pipe should be disabled
+ * (this code works with only 2 layers)
+ */
+ if (chan == 0) {
+ if (readl_relaxed(chan_io + VI_CFGx_ATTR(1 - layer)) == 0)
+ chan_disable = 1;
+ } else {
+ if (readl_relaxed(chan_io + UI_CFGx_ATTR(1 - layer)) == 0)
+ chan_disable = 1;
+ }
+
+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ if (chan == 0)
+ writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
+ else
+ writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
+
+ /* if no more layer in this channel, disable the pipe */
+ if (chan_disable) {
+ writel_relaxed(fcolor &
+ ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+ }
+}
+
+static void de2_drm_plane_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_crtc *crtc = state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct priv *priv = lcd->priv;
+ int plane_num = plane - lcd->planes;
+
+ /* if the crtc is disabled, mark update delayed */
+ if (!(priv->started & (1 << lcd->mixer))) {
+ lcd->delayed |= 1 << plane_num;
+ return; /* mixer disabled */
+ }
+
+ mutex_lock(&priv->mutex);
+
+ de2_plane_update(priv, lcd, plane_num, state, old_state);
+
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_drm_plane_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_crtc *crtc = old_state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct priv *priv = lcd->priv;
+ int plane_num = plane - lcd->planes;
+
+ if (!(priv->started & (1 << lcd->mixer)))
+ return; /* mixer disabled */
+
+ mutex_lock(&priv->mutex);
+
+ de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
+
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+ .atomic_update = de2_drm_plane_update,
+ .atomic_disable = de2_drm_plane_disable,
+};
+
+static const struct drm_plane_funcs plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int de2_one_plane_init(struct drm_device *drm,
+ struct drm_plane *plane,
+ int possible_crtcs,
+ int plane_num)
+{
+ int ret;
+
+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,
+ &plane_funcs,
+ plane_tb[plane_num].formats,
+ plane_tb[plane_num].n_formats,
+ plane_tb[plane_num].type, NULL);
+ if (ret >= 0)
+ drm_plane_helper_add(plane, &plane_helper_funcs);
+
+ return ret;
+}
+
+/* initialize the planes */
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
+{
+ int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
+
+ n = ARRAY_SIZE(plane_tb);
+ if (n != DE2_N_PLANES) {
+ dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
+ __stringify(DE2_N_PLANES) "\n", n);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < n; i++) {
+ ret = de2_one_plane_init(drm, &lcd->planes[i],
+ possible_crtcs, i);
+ if (ret < 0) {
+ dev_err(lcd->dev, "plane init failed %d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
--
2.10.2
^ permalink raw reply related
* [PATCH v6 2/5] drm: sun8i: add HDMI video support to A83T and H3
From: Jean-Francois Moine @ 2016-11-20 9:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1479641523.git.moinejf@free.fr>
This patch adds a HDMI video driver to the Allwinner's SoCs A83T and H3.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
.../devicetree/bindings/display/sunxi/hdmi.txt | 53 ++
drivers/gpu/drm/sun8i/Kconfig | 7 +
drivers/gpu/drm/sun8i/Makefile | 2 +
drivers/gpu/drm/sun8i/de2_hdmi.c | 394 ++++++++++
drivers/gpu/drm/sun8i/de2_hdmi.h | 51 ++
drivers/gpu/drm/sun8i/de2_hdmi_io.c | 839 +++++++++++++++++++++
6 files changed, 1346 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c
diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..85709ab
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,53 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+ - compatible : should be one of
+ "allwinner,sun8i-a83t-hdmi"
+ "allwinner,sun8i-h3-hdmi"
+ - clocks : phandles to the HDMI clocks as described in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+ "gate" : bus gate
+ "clock" : streaming clock
+ "ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : when 2 phandles, must be
+ "hdmi0" and "hdmi1"
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+ as defined in Documentation/devicetree/bindings/graph.txt.
+ port at 0 is video and port at 1 is audio.
+
+Example:
+
+ hdmi: hdmi at 01ee0000 {
+ compatible = "allwinner,sun8i-a83t-hdmi";
+ reg = <0x01ee0000 0x20000>;
+ clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+ <&ccu CLK_HDMI_DDC>;
+ clock-names = "gate", "clock", "ddc-clock";
+ resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+ reset-names = "hdmi0", "hdmi1";
+ ...
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port at 0 { /* video */
+ reg = <0>;
+ hdmi_lcd1: endpoint {
+ remote-endpoint = <&lcd1_hdmi>;
+ };
+ };
+ port at 1 { /* audio */
+ reg = <1>;
+ hdmi_i2s2: endpoint {
+ remote-endpoint = <&i2s2_hdmi>;
+ };
+ };
+ };
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_DE2
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+ tristate "Support for DE2 HDMI"
+ depends on DRM_SUN8I_DE2
+ help
+ Choose this option if you use want HDMI on DE2.
+ If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
#
sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9898a12
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,394 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * 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/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-hdmi",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-hdmi",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, encoder)
+
+/* --- encoder functions --- */
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+ struct clk *parent_clk;
+ u32 parent_rate;
+ int ret;
+
+ priv->cea_mode = drm_match_cea_mode(mode);
+
+ DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+ /* determine and set the best rate for the parent clock (pll-video) */
+ if ((270000 * 2) % mode->clock == 0)
+ parent_rate = 270000000;
+ else if (297000 % mode->clock == 0)
+ parent_rate = 297000000;
+ else
+ return; /* "640x480" rejected */
+ parent_clk = clk_get_parent(priv->clk);
+
+ ret = clk_set_rate(parent_clk, parent_rate);
+ if (ret) {
+ dev_err(priv->dev, "set parent rate failed %d\n", ret);
+ return;
+ }
+ ret = clk_set_rate(priv->clk, mode->clock * 1000);
+ if (ret)
+ dev_err(priv->dev, "set rate failed %d\n", ret);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_mode(priv, mode);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+ .mode_set = de2_hdmi_encoder_mode_set,
+ .enable = de2_hdmi_encoder_enable,
+ .disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int cea_mode = drm_match_cea_mode(mode);
+
+ if (hdmi_io_mode_valid(cea_mode) < 0)
+ return MODE_NOMODE;
+
+ return MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_get_hpd(priv);
+ mutex_unlock(&priv->mutex);
+
+ return ret ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+ unsigned int blk, size_t length)
+{
+ struct de2_hdmi_priv *priv = data;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_ddc_read(priv,
+ blk / 2, (blk & 1) ? 128 : 0,
+ length, buf);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ struct edid *edid;
+ int n;
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (!edid) {
+ dev_warn(priv->dev, "failed to read EDID\n");
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ return drm_add_modes_noedid(connector,
+ connector->cmdline_mode.xres,
+ connector->cmdline_mode.yres);
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+ connector->eld[0] ? "HDMI" : "DVI", n);
+
+ return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+ .get_modes = de2_hdmi_connector_get_modes,
+ .mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = de2_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+ clk_disable_unprepare(priv->clk_ddc);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ reset_control_assert(priv->reset1);
+ reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = data;
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /* if no CRTC, delay */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ /* HDMI init */
+ ret = reset_control_deassert(priv->reset0);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(priv->reset1);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk_ddc);
+ if (ret)
+ goto err;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_init(priv);
+ mutex_unlock(&priv->mutex);
+
+ /* encoder init */
+ ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ goto err;
+
+ drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+ /* connector init */
+ ret = drm_connector_init(drm, connector,
+ &de2_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ drm_connector_helper_add(connector,
+ &de2_hdmi_connector_helper_funcs);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+err_connector:
+ drm_encoder_cleanup(encoder);
+err:
+ dev_err(dev, "err %d\n", ret);
+ return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->connector.dev)
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+ .bind = de2_hdmi_bind,
+ .unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct de2_hdmi_priv *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->dev = dev;
+
+ mutex_init(&priv->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -ENXIO;
+ }
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ dev_err(dev, "failed to map registers err %d\n", ret);
+ return ret;
+ }
+
+ priv->gate = devm_clk_get(dev, "bus");
+ if (IS_ERR(priv->gate)) {
+ ret = PTR_ERR(priv->gate);
+ dev_err(dev, "gate clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "hdmi clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+ if (IS_ERR(priv->clk_ddc)) {
+ ret = PTR_ERR(priv->clk_ddc);
+ dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+ if (IS_ERR(priv->reset0)) {
+ ret = PTR_ERR(priv->reset0);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+ if (IS_ERR(priv->reset1)) {
+ ret = PTR_ERR(priv->reset1);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+ &pdev->dev)->data;
+
+ return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &de2_hdmi_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+ .probe = de2_hdmi_probe,
+ .remove = de2_hdmi_remove,
+ .driver = {
+ .name = "sun8i-de2-hdmi",
+ .of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+ },
+};
+
+/* create the video HDMI driver */
+static int __init de2_hdmi_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&de2_hdmi_driver);
+
+ return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+ platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..1f0b1d9
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+ struct device *dev;
+ void __iomem *mmio;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct clk *clk;
+ struct clk *clk_ddc;
+ struct clk *gate;
+ struct reset_control *reset0;
+ struct reset_control *reset1;
+
+ struct mutex mutex;
+ u8 soc_type;
+ u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char offset,
+ int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..8c690bf
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,839 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * 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.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ * https://linux-sunxi.org/DWC_HDMI_Controller
+ * https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG 0x10010
+#define HDMI_PHY_CTRL_REG 0x10020
+#define HDMI_PHY_24_REG 0x10024
+#define HDMI_PHY_28_REG 0x10028
+#define HDMI_PHY_PLL_REG 0x1002c
+#define HDMI_PHY_CLK_REG 0x10030
+#define HDMI_PHY_34_REG 0x10034
+#define HDMI_PHY_STATUS_REG 0x10038
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH0 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define HDMI_PHY_CONF0_PDZ BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define HDMI_AUD_CONF0_SW_RESET 0x80
+#define HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define HDMI_AUD_INPUTCLKFS_64FS 0x04
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0b_HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0xcee1
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0d_HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x4ee3
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+#define R_7e0f_HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0xcee3
+#define R_7e10_HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x0ee4
+#define R_7e11_HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x0ee5
+
+#define VIC_720x480_60 2
+#define VIC_1280x720_60 4
+#define VIC_1920x1080i_60 5
+#define VIC_720x480i_60 6
+#define VIC_1920x1080_60 16
+#define VIC_720x576_50 17
+#define VIC_1280x720_50 19
+#define VIC_1920x1080i_50 20
+#define VIC_720x576i_50 21
+#define VIC_1920x1080_50 31
+#define VIC_1920x1080_24 32
+#define VIC_1920x1080_25 33
+#define VIC_1920x1080_30 34
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readl_relaxed(priv->mmio + addr);
+}
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+ priv->mmio + addr);
+}
+
+/* read lock/unlock functions */
+static inline void hdmi_lock_read(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_unlock_read(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+ u8 clkdis = priv->soc_type == SOC_H3 ?
+ ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+ hdmi_lock_read(priv);
+
+ /* software reset */
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0x00);
+ udelay(2);
+
+ /* mask all interrupts */
+ hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+ hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+ hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+ hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+ hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+ hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+ hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+ hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+ hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+ hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+ hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+ HDMI_A_HDCPCFG1_SWRESET);
+ hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+ hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+ hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+ hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+ hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+ hdmi_inner_init(priv);
+
+ hdmi_writeb(priv, 0x10000, 0x01);
+ hdmi_writeb(priv, 0x10001, 0x00);
+ hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, 0x10003, 0x00);
+ hdmi_writeb(priv, 0x10007, 0xa0);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
+ udelay(1);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+ hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+ hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+ hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+ hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+ int to_cnt;
+ u32 tmp;
+
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+ udelay(10);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+ usleep_range(40, 50);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+ usleep_range(100, 120);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+ to_cnt = 10;
+ while (1) {
+ if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+ break;
+ usleep_range(200, 250);
+ if (--to_cnt == 0) {
+ pr_warn("hdmi phy init timeout\n");
+ break;
+ }
+ }
+
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+ hdmi_inner_init(priv);
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+ int addr, u8 valh, u8 vall)
+{
+ hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+ hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+ hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ usleep_range(2000, 2500);
+}
+
+static int get_divider(int rate)
+{
+ if (rate <= 27000)
+ return 11;
+ if (rate <= 74250)
+ return 4;
+ if (rate <= 148500)
+ return 2;
+ return 1;
+}
+
+static void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+/* case 11: * 480P/576P */
+ default:
+ hdmi_i2cm_write(priv, 0x06, 0x01,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0xe3 : 0xe0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ }
+ hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ u32 tmp;
+
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(200);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ if (tmp < 0x3d)
+ tmp += 2;
+ else
+ tmp = 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ msleep(100);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ default:
+/* case 11: * 480P/576P */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ }
+}
+
+/* HDMI functions */
+
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_phy_init_h3(priv);
+ else
+ hdmi_phy_init_a83t(priv);
+
+ /* hpd reset */
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* video init */
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ int avi_d2; /* AVI InfoFrame Data Byte 2 */
+ int h_blank, v_blank, h_sync_w, h_front_p;
+ int invidconf;
+
+ /* colorimetry and aspect ratio */
+ switch (priv->cea_mode) {
+ case VIC_720x480_60:
+ case VIC_720x480i_60:
+ case VIC_720x576_50:
+ case VIC_720x576i_50:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+ (HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+ break;
+ default:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+ (HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+ break;
+ }
+
+ h_blank = mode->htotal - mode->hdisplay;
+ v_blank = mode->vtotal - mode->vdisplay;
+ h_sync_w = mode->hsync_end - mode->hsync_start;
+ h_front_p = mode->hsync_start - mode->hdisplay;
+
+ invidconf = 0;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ invidconf |= 0x01;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ invidconf |= 0x20;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ invidconf |= 0x40;
+
+ if (priv->soc_type == SOC_H3) {
+ hdmi_phy_set_h3(priv, mode);
+ hdmi_inner_init(priv);
+ } else {
+ hdmi_io_init(priv);
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+ hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+ invidconf |
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+ hdmi_writeb(priv, 0x10001,
+ invidconf < 0x60 ? 0x03 : 0x00);
+ hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1,
+ mode->hdisplay >> 8);
+ hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+ mode->vsync_end - mode->vsync_start);
+ hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1,
+ mode->vdisplay >> 8);
+ hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1,
+ h_blank >> 8);
+ hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+ mode->vsync_start - mode->vdisplay);
+ hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1,
+ h_front_p >> 8);
+ hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1,
+ h_sync_w >> 8);
+ hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0,
+ mode->hdisplay);
+ hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0,
+ h_blank);
+ hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0,
+ h_front_p);
+ hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0,
+ h_sync_w);
+ hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0,
+ mode->vdisplay);
+ hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+ v_blank);
+ hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+ hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+ hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+ hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+ hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+ hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+ hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+ hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x41 : 0x40);
+ hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+ hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+ hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+ hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+ hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+ hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+ hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+ hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+ hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+ hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+ hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+ if (priv->connector.eld[0]) { /* if audio/HDMI */
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+ hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+ hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+ HDMI_IEEE_OUI >> 8);
+ hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+ HDMI_IEEE_OUI & 0xff);
+ hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+ hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+ hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+ hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+ hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+ hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+ hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+ hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+ hdmi_lock_read(priv);
+ hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF, 0x08);
+ hdmi_unlock_read(priv);
+
+ /* AVI */
+ hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+ hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+
+ hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+ hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+ }
+
+ hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ if (priv->soc_type != SOC_H3)
+ hdmi_phy_set_a83t(priv, mode);
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+ return 0;
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char off,
+ int nbyte, char *pbuf)
+{
+ unsigned int to_cnt;
+ u8 reg;
+ int ret = 0;
+
+ hdmi_lock_read(priv);
+ hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+ to_cnt = 50;
+ while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+ udelay(10);
+ if (--to_cnt == 0) { /* wait for 500us for timeout */
+ pr_warn("hdmi ddc reset timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+ hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+ hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+ while (nbyte > 0) {
+ hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+ hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+ hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+ hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+ hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+ HDMI_I2CM_OPERATION_DDC_READ);
+
+ to_cnt = 200; /* timeout 100ms */
+ while (1) {
+ reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+ if (reg & 0x02) {
+ *pbuf++ = hdmi_readb(priv,
+ R_7e03_HDMI_I2CM_DATAI);
+ break;
+ }
+ if (reg & 0x01) {
+ pr_warn("hdmi ddc read error\n");
+ ret = -1;
+ break;
+ }
+ if (--to_cnt == 0) {
+ if (!ret) {
+ pr_warn("hdmi ddc read timeout\n");
+ ret = -1;
+ }
+ break;
+ }
+ usleep_range(500, 800);
+ }
+ if (ret)
+ break;
+ nbyte--;
+ off++;
+ }
+ hdmi_unlock_read(priv);
+
+ return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+ int ret;
+
+ hdmi_lock_read(priv);
+
+ if (priv->soc_type == SOC_H3)
+ ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+ else
+ ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+ hdmi_unlock_read(priv);
+
+ return ret != 0;
+}
+
+int hdmi_io_mode_valid(int cea_mode)
+{
+ /* check the known working resolutions */
+ switch (cea_mode) {
+ case VIC_720x480_60:
+ case VIC_1280x720_60:
+ case VIC_1920x1080i_60:
+ case VIC_720x480i_60:
+ case VIC_1920x1080_60:
+ case VIC_720x576_50:
+ case VIC_1280x720_50:
+ case VIC_1920x1080i_50:
+ case VIC_720x576i_50:
+ case VIC_1920x1080_50:
+ case VIC_1920x1080_24:
+ case VIC_1920x1080_25:
+ case VIC_1920x1080_30:
+ return 1;
+ }
+ return -1;
+}
--
2.10.2
^ permalink raw reply related
* [PATCH v4 0/3] dmaengine: sun6i: add the support for the Allwinner A64 SOC.
From: Hao Zhang @ 2016-11-20 10:45 UTC (permalink / raw)
To: linux-arm-kernel
Abort Allwinner A64 SOC :
Allwinner A64 (sun50i) 64bit SoC features a Quad-Core Cortex-A53 ARM CPU,
and a Mali400 MP2 GPU from ARM.
It has 8 channel DMA which flexible
data width of 8/16/32/64-bit. Detailed info about it is on
Allwinner_A64_User_Manual_V1.0 page 196 and A64_Datasheet_V1.1 page 8.
Document:
A64 Datasheet v1.1: http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf
A64 User Manual v1.0: http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf
Abort Pine64:
The Pine64 is a cost-optimized board with SOC A64 sporting ARMv8 (64-bit ARM) capable cores.
Detail info :
https://linux-sunxi.org/Pine64
http://wiki.pine64.org/index.php/Main_Page
PATCH LOG:
PATCH[v3,1/3]: https://patchwork.kernel.org/patch/9415765/
PATCH[v3,2/3]: https://patchwork.kernel.org/patch/9415761/
PATCH[V3,3/3]: https://patchwork.kernel.org/patch/9415761/
Changes in v4 :
PATCH[v4,1/3]:
Since PATCH[v3,1/3] has Acked-by Maxime Ripard and Rob Herring
this is resend it.
PATCH[v4,2/3]:
Accept the comments from Maxime Ripard and add the dma nodes by
base adress, thinks!
PATCH[V4,3/3]:
Accept the hint from Maxime Ripard, add the 8 bytes bus width for
A64 dma and add the buswidth test, thinks!
It has test on Pine64 using dmatest.
Regards :)
Hao Zhang (3):
Documentation: DT: add dma compatible for sun50i A64 SOC
ARM64: dts: sun6i: add dma node for a64.
dmaengine: sun6i: share the dma driver with sun50i
.../devicetree/bindings/dma/sun6i-dma.txt | 1 +
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 ++++++
drivers/dma/sun6i-dma.c | 33 +++++++++++++++++++++-
3 files changed, 42 insertions(+), 1 deletion(-)
--
2.7.4
^ permalink raw reply
* [PATCH v4 1/3] Documentation: DT: add dma compatible for sun50i A64 SOC
From: Hao Zhang @ 2016-11-20 10:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479638740-20520-1-git-send-email-hao5781286@gmail.com>
This add the property of Allwinner sun50i A64 dma.
Signed-off-by: Hao Zhang <hao5781286@gmail.com>
---
Documentation/devicetree/bindings/dma/sun6i-dma.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
index 6b26704..4398b99 100644
--- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt
+++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
@@ -9,6 +9,7 @@ Required properties:
"allwinner,sun8i-a23-dma"
"allwinner,sun8i-a83t-dma"
"allwinner,sun8i-h3-dma"
+ "allwinner,sun50i-a64-dma"
- reg: Should contain the registers base address and length
- interrupts: Should contain a reference to the interrupt used by this device
- clocks: Should contain a reference to the parent AHB clock
--
2.7.4
^ permalink raw reply related
* [PATCH v4 2/3] ARM64: dts: sun6i: add dma node for a64.
From: Hao Zhang @ 2016-11-20 10:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479638740-20520-1-git-send-email-hao5781286@gmail.com>
Accroding to the Allwinner_A64_User_Manual_V1.0 P198
the DMA base address is 0x01c02000.
Signed-off-by: Hao Zhang <hao5781286@gmail.com>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index e3c3d7d8..403bbfa 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -122,6 +122,15 @@
#size-cells = <1>;
ranges;
+ dma: dma-controller at 1c02000 {
+ compatible = "allwinner,sun50i-a64-dma";
+ reg = <0x01c02000 0x1000>;
+ interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_DMA>;
+ resets = <&ccu RST_BUS_DMA>;
+ #dma-cells = <1>;
+ };
+
ccu: clock at 01c20000 {
compatible = "allwinner,sun50i-a64-ccu";
reg = <0x01c20000 0x400>;
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox