--- /dev/null
+From 69275b667bd930cf5d5f577ba0ab1987c9d13987 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Mon, 21 Aug 2023 23:29:15 +0800
+Subject: [PATCH 001/116] clk: starfive: jh7110-sys: Fix lower rate of CPUfreq
+ by setting PLL0 rate to 1.5GHz
+
+CPUfreq supports 4 cpu frequency loads on 375/500/750/1500MHz.
+But now PLL0 rate is 1GHz and the cpu frequency loads become
+333/500/500/1000MHz in fact.
+
+So PLL0 rate should be set to 1.5GHz. Change the parent of cpu_root clock
+and the divider of cpu_core before the setting.
+
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Fixes: e2c510d6d630 ("riscv: dts: starfive: Add cpu scaling for JH7110 SoC")
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ .../clk/starfive/clk-starfive-jh7110-sys.c | 47 ++++++++++++++++++-
+ 1 file changed, 46 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c
+@@ -501,7 +501,52 @@ static int __init jh7110_syscrg_probe(st
+ if (ret)
+ return ret;
+
+- return jh7110_reset_controller_register(priv, "rst-sys", 0);
++ ret = jh7110_reset_controller_register(priv, "rst-sys", 0);
++ if (ret)
++ return ret;
++
++ /*
++ * Set PLL0 rate to 1.5GHz
++ * In order to not affect the cpu when the PLL0 rate is changing,
++ * we need to switch the parent of cpu_root clock to osc clock first,
++ * and then switch back after setting the PLL0 rate.
++ */
++ pllclk = clk_get(priv->dev, "pll0_out");
++ if (!IS_ERR(pllclk)) {
++ struct clk *osc = clk_get(&pdev->dev, "osc");
++ struct clk *cpu_root = priv->reg[JH7110_SYSCLK_CPU_ROOT].hw.clk;
++ struct clk *cpu_core = priv->reg[JH7110_SYSCLK_CPU_CORE].hw.clk;
++
++ if (IS_ERR(osc)) {
++ clk_put(pllclk);
++ return PTR_ERR(osc);
++ }
++
++ /*
++ * CPU need voltage regulation by CPUfreq if set 1.5GHz.
++ * So in this driver, cpu_core need to be set the divider to be 2 first
++ * and will be 750M after setting parent.
++ */
++ ret = clk_set_rate(cpu_core, clk_get_rate(cpu_core) / 2);
++ if (ret)
++ goto failed_set;
++
++ ret = clk_set_parent(cpu_root, osc);
++ if (ret)
++ goto failed_set;
++
++ ret = clk_set_rate(pllclk, 1500000000);
++ if (ret)
++ goto failed_set;
++
++ ret = clk_set_parent(cpu_root, pllclk);
++
++failed_set:
++ clk_put(pllclk);
++ clk_put(osc);
++ }
++
++ return ret;
+ }
+
+ static const struct of_device_id jh7110_syscrg_match[] = {
--- /dev/null
+From 7d0dbcbc079e4f72b69f53442b7759da6ebc4f87 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 19 Oct 2023 13:34:59 +0800
+Subject: [PATCH 002/116] dt-bindings: timer: Add timer for StarFive JH7110 SoC
+
+Add bindings for the timer on the JH7110 RISC-V SoC
+by StarFive Technology Ltd.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ .../bindings/timer/starfive,jh7110-timer.yaml | 96 +++++++++++++++++++
+ 1 file changed, 96 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
+@@ -0,0 +1,96 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 Timer
++
++maintainers:
++ - Xingyu Wu <xingyu.wu@starfivetech.com>
++ - Samin Guo <samin.guo@starfivetech.com>
++
++description:
++ This timer has four free-running 32 bit counters in StarFive JH7110 SoC.
++ And each channel(counter) triggers an interrupt when timeout. They support
++ one-shot mode and continuous-run mode.
++
++properties:
++ compatible:
++ const: starfive,jh7110-timer
++
++ reg:
++ maxItems: 1
++
++ interrupts:
++ items:
++ - description: channel 0
++ - description: channel 1
++ - description: channel 2
++ - description: channel 3
++
++ clocks:
++ items:
++ - description: timer APB
++ - description: channel 0
++ - description: channel 1
++ - description: channel 2
++ - description: channel 3
++
++ clock-names:
++ items:
++ - const: apb
++ - const: ch0
++ - const: ch1
++ - const: ch2
++ - const: ch3
++
++ resets:
++ items:
++ - description: timer APB
++ - description: channel 0
++ - description: channel 1
++ - description: channel 2
++ - description: channel 3
++
++ reset-names:
++ items:
++ - const: apb
++ - const: ch0
++ - const: ch1
++ - const: ch2
++ - const: ch3
++
++required:
++ - compatible
++ - reg
++ - interrupts
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
++
++additionalProperties: false
++
++examples:
++ - |
++ timer@13050000 {
++ compatible = "starfive,jh7110-timer";
++ reg = <0x13050000 0x10000>;
++ interrupts = <69>, <70>, <71> ,<72>;
++ clocks = <&clk 124>,
++ <&clk 125>,
++ <&clk 126>,
++ <&clk 127>,
++ <&clk 128>;
++ clock-names = "apb", "ch0", "ch1",
++ "ch2", "ch3";
++ resets = <&rst 117>,
++ <&rst 118>,
++ <&rst 119>,
++ <&rst 120>,
++ <&rst 121>;
++ reset-names = "apb", "ch0", "ch1",
++ "ch2", "ch3";
++ };
++
--- /dev/null
+From 7cb47848f8a10aed6e050c0ea483b4bb5eaa62a4 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 19 Oct 2023 13:35:00 +0800
+Subject: [PATCH 003/116] clocksource: Add JH7110 timer driver
+
+Add timer driver for the StarFive JH7110 SoC.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ drivers/clocksource/Kconfig | 11 +
+ drivers/clocksource/Makefile | 1 +
+ drivers/clocksource/timer-jh7110.c | 380 +++++++++++++++++++++++++++++
+ 3 files changed, 392 insertions(+)
+ create mode 100644 drivers/clocksource/timer-jh7110.c
+
+--- a/drivers/clocksource/Kconfig
++++ b/drivers/clocksource/Kconfig
+@@ -641,6 +641,17 @@ config RISCV_TIMER
+ is accessed via both the SBI and the rdcycle instruction. This is
+ required for all RISC-V systems.
+
++config STARFIVE_JH7110_TIMER
++ bool "Timer for the STARFIVE JH7110 SoC"
++ depends on ARCH_STARFIVE || COMPILE_TEST
++ select TIMER_OF
++ select CLKSRC_MMIO
++ default ARCH_STARFIVE
++ help
++ This enables the timer for StarFive JH7110 SoC. On RISC-V platform,
++ the system has started RISCV_TIMER, but you can also use this timer
++ which can provide four channels to do a lot more things on JH7110 SoC.
++
+ config CLINT_TIMER
+ bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST
+ depends on GENERIC_SCHED_CLOCK && RISCV
+--- a/drivers/clocksource/Makefile
++++ b/drivers/clocksource/Makefile
+@@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-
+ obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
+ obj-$(CONFIG_X86_NUMACHIP) += numachip.o
+ obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o
++obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o
+ obj-$(CONFIG_CLINT_TIMER) += timer-clint.o
+ obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
+ obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
+--- /dev/null
++++ b/drivers/clocksource/timer-jh7110.c
+@@ -0,0 +1,380 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Starfive JH7110 Timer driver
++ *
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
++ *
++ * Author:
++ * Xingyu Wu <xingyu.wu@starfivetech.com>
++ * Samin Guo <samin.guo@starfivetech.com>
++ */
++
++#include <linux/clk.h>
++#include <linux/clockchips.h>
++#include <linux/clocksource.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/irq.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/sched_clock.h>
++
++/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */
++#define JH7110_TIMER_CH_LEN 0x40
++#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN)
++#define JH7110_TIMER_CH_MAX 4
++
++#define JH7110_CLOCK_SOURCE_RATING 200
++#define JH7110_VALID_BITS 32
++#define JH7110_DELAY_US 0
++#define JH7110_TIMEOUT_US 10000
++#define JH7110_CLOCKEVENT_RATING 300
++#define JH7110_TIMER_MAX_TICKS 0xffffffff
++#define JH7110_TIMER_MIN_TICKS 0xf
++#define JH7110_TIMER_RELOAD_VALUE 0
++
++#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */
++#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */
++#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */
++#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */
++#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */
++#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */
++#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear register */
++#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */
++
++#define JH7110_TIMER_INT_CLR_ENA BIT(0)
++#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1)
++
++struct jh7110_clkevt {
++ struct clock_event_device evt;
++ struct clocksource cs;
++ bool cs_is_valid;
++ struct clk *clk;
++ struct reset_control *rst;
++ u32 rate;
++ u32 reload_val;
++ void __iomem *base;
++ char name[sizeof("jh7110-timer.chX")];
++};
++
++struct jh7110_timer_priv {
++ struct clk *pclk;
++ struct reset_control *prst;
++ struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX];
++};
++
++/* 0:continuous-run mode, 1:single-run mode */
++enum jh7110_timer_mode {
++ JH7110_TIMER_MODE_CONTIN,
++ JH7110_TIMER_MODE_SINGLE,
++};
++
++/* Interrupt Mask, 0:Unmask, 1:Mask */
++enum jh7110_timer_int_mask {
++ JH7110_TIMER_INT_ENA,
++ JH7110_TIMER_INT_DIS,
++};
++
++enum jh7110_timer_enable {
++ JH7110_TIMER_DIS,
++ JH7110_TIMER_ENA,
++};
++
++static inline struct jh7110_clkevt *to_jh7110_clkevt(struct clock_event_device *evt)
++{
++ return container_of(evt, struct jh7110_clkevt, evt);
++}
++
++/*
++ * BIT(0): Read value represent channel int status.
++ * Write 1 to this bit to clear interrupt. Write 0 has no effects.
++ * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written.
++ */
++static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt)
++{
++ u32 value;
++ int ret;
++
++ /* Waiting interrupt can be cleared */
++ ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value,
++ !(value & JH7110_TIMER_INT_CLR_AVA_MASK),
++ JH7110_DELAY_US, JH7110_TIMEOUT_US);
++ if (!ret)
++ writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + JH7110_TIMER_INT_CLR);
++
++ return ret;
++}
++
++static int jh7110_timer_start(struct jh7110_clkevt *clkevt)
++{
++ int ret;
++
++ /* Disable and clear interrupt first */
++ writel(JH7110_TIMER_INT_DIS, clkevt->base + JH7110_TIMER_INT_MASK);
++ ret = jh7110_timer_int_clear(clkevt);
++ if (ret)
++ return ret;
++
++ writel(JH7110_TIMER_INT_ENA, clkevt->base + JH7110_TIMER_INT_MASK);
++ writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE);
++
++ return 0;
++}
++
++static int jh7110_timer_shutdown(struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE);
++ return jh7110_timer_int_clear(clkevt);
++}
++
++static void jh7110_timer_suspend(struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ clkevt->reload_val = readl(clkevt->base + JH7110_TIMER_LOAD);
++ jh7110_timer_shutdown(evt);
++}
++
++static void jh7110_timer_resume(struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ writel(clkevt->reload_val, clkevt->base + JH7110_TIMER_LOAD);
++ writel(JH7110_TIMER_RELOAD_VALUE, clkevt->base + JH7110_TIMER_RELOAD);
++ jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_tick_resume(struct clock_event_device *evt)
++{
++ jh7110_timer_resume(evt);
++
++ return 0;
++}
++
++/* IRQ handler for the timer */
++static irqreturn_t jh7110_timer_interrupt(int irq, void *priv)
++{
++ struct clock_event_device *evt = (struct clock_event_device *)priv;
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ if (jh7110_timer_int_clear(clkevt))
++ return IRQ_NONE;
++
++ if (evt->event_handler)
++ evt->event_handler(evt);
++
++ return IRQ_HANDLED;
++}
++
++static int jh7110_timer_set_periodic(struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++ u32 periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ);
++
++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL);
++ writel(periodic, clkevt->base + JH7110_TIMER_LOAD);
++
++ return jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_set_oneshot(struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL);
++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD);
++
++ return jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_set_next_event(unsigned long next,
++ struct clock_event_device *evt)
++{
++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL);
++ writel(next, clkevt->base + JH7110_TIMER_LOAD);
++
++ return jh7110_timer_start(clkevt);
++}
++
++static void jh7110_set_clockevent(struct clock_event_device *evt)
++{
++ evt->features = CLOCK_EVT_FEAT_PERIODIC |
++ CLOCK_EVT_FEAT_ONESHOT |
++ CLOCK_EVT_FEAT_DYNIRQ;
++ evt->set_state_shutdown = jh7110_timer_shutdown;
++ evt->set_state_periodic = jh7110_timer_set_periodic;
++ evt->set_state_oneshot = jh7110_timer_set_oneshot;
++ evt->set_state_oneshot_stopped = jh7110_timer_shutdown;
++ evt->tick_resume = jh7110_timer_tick_resume;
++ evt->set_next_event = jh7110_timer_set_next_event;
++ evt->suspend = jh7110_timer_suspend;
++ evt->resume = jh7110_timer_resume;
++ evt->rating = JH7110_CLOCKEVENT_RATING;
++}
++
++static u64 jh7110_timer_clocksource_read(struct clocksource *cs)
++{
++ struct jh7110_clkevt *clkevt = container_of(cs, struct jh7110_clkevt, cs);
++
++ return (u64)readl(clkevt->base + JH7110_TIMER_VALUE);
++}
++
++static int jh7110_clocksource_init(struct jh7110_clkevt *clkevt)
++{
++ int ret;
++
++ clkevt->cs.name = clkevt->name;
++ clkevt->cs.rating = JH7110_CLOCK_SOURCE_RATING;
++ clkevt->cs.read = jh7110_timer_clocksource_read;
++ clkevt->cs.mask = CLOCKSOURCE_MASK(JH7110_VALID_BITS);
++ clkevt->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
++
++ ret = clocksource_register_hz(&clkevt->cs, clkevt->rate);
++ if (ret)
++ return ret;
++
++ clkevt->cs_is_valid = true; /* clocksource register done */
++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL);
++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD);
++
++ return jh7110_timer_start(clkevt);
++}
++
++static void jh7110_clockevents_register(struct jh7110_clkevt *clkevt)
++{
++ clkevt->rate = clk_get_rate(clkevt->clk);
++
++ jh7110_set_clockevent(&clkevt->evt);
++ clkevt->evt.name = clkevt->name;
++ clkevt->evt.cpumask = cpu_possible_mask;
++
++ clockevents_config_and_register(&clkevt->evt, clkevt->rate,
++ JH7110_TIMER_MIN_TICKS, JH7110_TIMER_MAX_TICKS);
++}
++
++static void jh7110_timer_release(void *data)
++{
++ struct jh7110_timer_priv *priv = data;
++ int i;
++
++ for (i = 0; i < JH7110_TIMER_CH_MAX; i++) {
++ /* Disable each channel of timer */
++ if (priv->clkevt[i].base)
++ writel(JH7110_TIMER_DIS, priv->clkevt[i].base + JH7110_TIMER_ENABLE);
++
++ /* Avoid no initialization in the loop of the probe */
++ if (!IS_ERR_OR_NULL(priv->clkevt[i].rst))
++ reset_control_assert(priv->clkevt[i].rst);
++
++ if (priv->clkevt[i].cs_is_valid)
++ clocksource_unregister(&priv->clkevt[i].cs);
++ }
++
++ reset_control_assert(priv->prst);
++}
++
++static int jh7110_timer_probe(struct platform_device *pdev)
++{
++ struct jh7110_timer_priv *priv;
++ struct jh7110_clkevt *clkevt;
++ char name[sizeof("chX")];
++ int ch;
++ int ret;
++ void __iomem *base;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(base))
++ return dev_err_probe(&pdev->dev, PTR_ERR(base),
++ "failed to map registers\n");
++
++ priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb");
++ if (IS_ERR(priv->prst))
++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst),
++ "failed to get apb reset\n");
++
++ priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb");
++ if (IS_ERR(priv->pclk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk),
++ "failed to get & enable apb clock\n");
++
++ ret = reset_control_deassert(priv->prst);
++ if (ret)
++ return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n");
++
++ ret = devm_add_action_or_reset(&pdev->dev, jh7110_timer_release, priv);
++ if (ret)
++ return ret;
++
++ for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) {
++ clkevt = &priv->clkevt[ch];
++ snprintf(name, sizeof(name), "ch%d", ch);
++
++ clkevt->base = base + JH7110_TIMER_CH_BASE(ch);
++ /* Ensure timer is disabled */
++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE);
++
++ clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name);
++ if (IS_ERR(clkevt->rst))
++ return PTR_ERR(clkevt->rst);
++
++ clkevt->clk = devm_clk_get_enabled(&pdev->dev, name);
++ if (IS_ERR(clkevt->clk))
++ return PTR_ERR(clkevt->clk);
++
++ ret = reset_control_deassert(clkevt->rst);
++ if (ret)
++ return ret;
++
++ clkevt->evt.irq = platform_get_irq(pdev, ch);
++ if (clkevt->evt.irq < 0)
++ return clkevt->evt.irq;
++
++ snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", ch);
++ jh7110_clockevents_register(clkevt);
++
++ ret = devm_request_irq(&pdev->dev, clkevt->evt.irq, jh7110_timer_interrupt,
++ IRQF_TIMER | IRQF_IRQPOLL,
++ clkevt->name, &clkevt->evt);
++ if (ret)
++ return ret;
++
++ ret = jh7110_clocksource_init(clkevt);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct of_device_id jh7110_timer_match[] = {
++ { .compatible = "starfive,jh7110-timer", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7110_timer_match);
++
++static struct platform_driver jh7110_timer_driver = {
++ .probe = jh7110_timer_probe,
++ .driver = {
++ .name = "jh7110-timer",
++ .of_match_table = jh7110_timer_match,
++ },
++};
++module_platform_driver(jh7110_timer_driver);
++
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive JH7110 timer driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From b513eb2cabee212ba1a23839f18c0026a21e653e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Sep 2023 14:28:32 +0800
+Subject: [PATCH 004/116] dt-bindings: mmc: starfive: Remove properties from
+ required
+
+Due to the change of tuning implementation, it's no longer necessary to
+use the "starfive,sysreg" property in dts, so remove it from required.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Link: https://lore.kernel.org/r/20230922062834.39212-2-william.qiu@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml
++++ b/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml
+@@ -55,7 +55,6 @@ required:
+ - clocks
+ - clock-names
+ - interrupts
+- - starfive,sysreg
+
+ unevaluatedProperties: false
+
+@@ -73,5 +72,4 @@ examples:
+ fifo-depth = <32>;
+ fifo-watermark-aligned;
+ data-addr = <0>;
+- starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>;
+ };
--- /dev/null
+From 3ae8cec8fd28e18847edb67dfea04718c2f3369f Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Sep 2023 14:28:33 +0800
+Subject: [PATCH 005/116] mmc: starfive: Change tuning implementation
+
+Before, we used syscon to achieve tuning, but the actual measurement
+showed little effect, so the tuning implementation was modified here,
+and it was realized by reading and writing the UHS_REG_EXT register.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Reviewed-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Link: https://lore.kernel.org/r/20230922062834.39212-3-william.qiu@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/mmc/host/dw_mmc-starfive.c | 137 +++++++++--------------------
+ 1 file changed, 40 insertions(+), 97 deletions(-)
+
+--- a/drivers/mmc/host/dw_mmc-starfive.c
++++ b/drivers/mmc/host/dw_mmc-starfive.c
+@@ -5,6 +5,7 @@
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ */
+
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/mfd/syscon.h>
+@@ -20,13 +21,7 @@
+ #define ALL_INT_CLR 0x1ffff
+ #define MAX_DELAY_CHAIN 32
+
+-struct starfive_priv {
+- struct device *dev;
+- struct regmap *reg_syscon;
+- u32 syscon_offset;
+- u32 syscon_shift;
+- u32 syscon_mask;
+-};
++#define STARFIVE_SMPL_PHASE GENMASK(20, 16)
+
+ static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+ {
+@@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(stru
+ }
+ }
+
++static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase)
++{
++ /* change driver phase and sample phase */
++ u32 reg_value = mci_readl(host, UHS_REG_EXT);
++
++ /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */
++ reg_value &= ~STARFIVE_SMPL_PHASE;
++ reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase);
++ mci_writel(host, UHS_REG_EXT, reg_value);
++
++ /* We should delay 1ms wait for timing setting finished. */
++ mdelay(1);
++}
++
+ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
+ u32 opcode)
+ {
+ static const int grade = MAX_DELAY_CHAIN;
+ struct dw_mci *host = slot->host;
+- struct starfive_priv *priv = host->priv;
+- int rise_point = -1, fall_point = -1;
+- int err, prev_err = 0;
+- int i;
+- bool found = 0;
+- u32 regval;
+-
+- /*
+- * Use grade as the max delay chain, and use the rise_point and
+- * fall_point to ensure the best sampling point of a data input
+- * signals.
+- */
+- for (i = 0; i < grade; i++) {
+- regval = i << priv->syscon_shift;
+- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+- priv->syscon_mask, regval);
+- if (err)
+- return err;
+- mci_writel(host, RINTSTS, ALL_INT_CLR);
+-
+- err = mmc_send_tuning(slot->mmc, opcode, NULL);
+- if (!err)
+- found = 1;
+-
+- if (i > 0) {
+- if (err && !prev_err)
+- fall_point = i - 1;
+- if (!err && prev_err)
+- rise_point = i;
+- }
++ int smpl_phase, smpl_raise = -1, smpl_fall = -1;
++ int ret;
+
+- if (rise_point != -1 && fall_point != -1)
+- goto tuning_out;
++ for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) {
++ dw_mci_starfive_set_sample_phase(host, smpl_phase);
++ mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+- prev_err = err;
+- err = 0;
+- }
++ ret = mmc_send_tuning(slot->mmc, opcode, NULL);
+
+-tuning_out:
+- if (found) {
+- if (rise_point == -1)
+- rise_point = 0;
+- if (fall_point == -1)
+- fall_point = grade - 1;
+- if (fall_point < rise_point) {
+- if ((rise_point + fall_point) >
+- (grade - 1))
+- i = fall_point / 2;
+- else
+- i = (rise_point + grade - 1) / 2;
+- } else {
+- i = (rise_point + fall_point) / 2;
++ if (!ret && smpl_raise < 0) {
++ smpl_raise = smpl_phase;
++ } else if (ret && smpl_raise >= 0) {
++ smpl_fall = smpl_phase - 1;
++ break;
+ }
+-
+- regval = i << priv->syscon_shift;
+- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+- priv->syscon_mask, regval);
+- if (err)
+- return err;
+- mci_writel(host, RINTSTS, ALL_INT_CLR);
+-
+- dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i);
+- } else {
+- dev_err(host->dev, "No valid delay chain! use default\n");
+- err = -EINVAL;
+ }
+
+- mci_writel(host, RINTSTS, ALL_INT_CLR);
+- return err;
+-}
+-
+-static int dw_mci_starfive_parse_dt(struct dw_mci *host)
+-{
+- struct of_phandle_args args;
+- struct starfive_priv *priv;
+- int ret;
++ if (smpl_phase >= grade)
++ smpl_fall = grade - 1;
+
+- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- ret = of_parse_phandle_with_fixed_args(host->dev->of_node,
+- "starfive,sysreg", 3, 0, &args);
+- if (ret) {
+- dev_err(host->dev, "Failed to parse starfive,sysreg\n");
+- return -EINVAL;
++ if (smpl_raise < 0) {
++ smpl_phase = 0;
++ dev_err(host->dev, "No valid delay chain! use default\n");
++ ret = -EINVAL;
++ goto out;
+ }
+
+- priv->reg_syscon = syscon_node_to_regmap(args.np);
+- of_node_put(args.np);
+- if (IS_ERR(priv->reg_syscon))
+- return PTR_ERR(priv->reg_syscon);
+-
+- priv->syscon_offset = args.args[0];
+- priv->syscon_shift = args.args[1];
+- priv->syscon_mask = args.args[2];
+-
+- host->priv = priv;
++ smpl_phase = (smpl_raise + smpl_fall) / 2;
++ dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase);
++ ret = 0;
+
+- return 0;
++out:
++ dw_mci_starfive_set_sample_phase(host, smpl_phase);
++ mci_writel(host, RINTSTS, ALL_INT_CLR);
++ return ret;
+ }
+
+ static const struct dw_mci_drv_data starfive_data = {
+ .common_caps = MMC_CAP_CMD23,
+ .set_ios = dw_mci_starfive_set_ios,
+- .parse_dt = dw_mci_starfive_parse_dt,
+ .execute_tuning = dw_mci_starfive_execute_tuning,
+ };
+
--- /dev/null
+From e366df2ff64e9f93a5b35eea6a198b005d5a0911 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Dec 2023 17:45:45 +0800
+Subject: [PATCH 006/116] dt-bindings: pwm: Add bindings for OpenCores PWM
+ Controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add bindings for OpenCores PWM Controller.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+---
+ .../bindings/pwm/opencores,pwm.yaml | 55 +++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+@@ -0,0 +1,55 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: OpenCores PWM controller
++
++maintainers:
++ - William Qiu <william.qiu@starfivetech.com>
++
++description:
++ The OpenCores PTC ip core contains a PWM controller. When operating in PWM
++ mode, the PTC core generates binary signal with user-programmable low and
++ high periods. All PTC counters and registers are 32-bit.
++
++allOf:
++ - $ref: pwm.yaml#
++
++properties:
++ compatible:
++ items:
++ - enum:
++ - starfive,jh7100-pwm
++ - starfive,jh7110-pwm
++ - const: opencores,pwm-v1
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ resets:
++ maxItems: 1
++
++ "#pwm-cells":
++ const: 3
++
++required:
++ - compatible
++ - reg
++ - clocks
++
++additionalProperties: false
++
++examples:
++ - |
++ pwm@12490000 {
++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
++ reg = <0x12490000 0x10000>;
++ clocks = <&clkgen 181>;
++ resets = <&rstgen 109>;
++ #pwm-cells = <3>;
++ };
--- /dev/null
+From 644bfe581dde9b762460a4916da4d71c148be06e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Dec 2023 17:45:46 +0800
+Subject: [PATCH 007/116] pwm: opencores: Add PWM driver support
+
+Add driver for OpenCores PWM Controller. And add compatibility code
+which based on StarFive SoC.
+
+Co-developed-by: Hal Feng <hal.feng@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ drivers/pwm/Kconfig | 12 ++
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-ocores.c | 233 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 246 insertions(+)
+ create mode 100644 drivers/pwm/pwm-ocores.c
+
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -434,6 +434,18 @@ config PWM_NTXEC
+ controller found in certain e-book readers designed by the original
+ design manufacturer Netronix.
+
++config PWM_OCORES
++ tristate "OpenCores PWM support"
++ depends on HAS_IOMEM && OF
++ depends on COMMON_CLK
++ depends on ARCH_STARFIVE || COMPILE_TEST
++ help
++ If you say yes to this option, support will be included for the
++ OpenCores PWM. For details see https://opencores.org/projects/ptc.
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-ocores.
++
+ config PWM_OMAP_DMTIMER
+ tristate "OMAP Dual-Mode Timer PWM support"
+ depends on OF
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-
+ obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o
+ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
+ obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o
++obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o
+ obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
+ obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
+ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
+--- /dev/null
++++ b/drivers/pwm/pwm-ocores.c
+@@ -0,0 +1,233 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * OpenCores PWM Driver
++ *
++ * https://opencores.org/projects/ptc
++ *
++ * Copyright (C) 2018-2023 StarFive Technology Co., Ltd.
++ *
++ * Limitations:
++ * - The hardware only do inverted polarity.
++ * - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns.
++ * - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns.
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++
++/* OCPWM_CTRL register bits*/
++#define REG_OCPWM_EN BIT(0)
++#define REG_OCPWM_ECLK BIT(1)
++#define REG_OCPWM_NEC BIT(2)
++#define REG_OCPWM_OE BIT(3)
++#define REG_OCPWM_SIGNLE BIT(4)
++#define REG_OCPWM_INTE BIT(5)
++#define REG_OCPWM_INT BIT(6)
++#define REG_OCPWM_CNTRRST BIT(7)
++#define REG_OCPWM_CAPTE BIT(8)
++
++struct ocores_pwm_device {
++ struct pwm_chip chip;
++ struct clk *clk;
++ struct reset_control *rst;
++ const struct ocores_pwm_data *data;
++ void __iomem *regs;
++ u32 clk_rate; /* PWM APB clock frequency */
++};
++
++struct ocores_pwm_data {
++ void __iomem *(*get_ch_base)(void __iomem *base, unsigned int channel);
++};
++
++static inline u32 ocores_readl(struct ocores_pwm_device *ddata,
++ unsigned int channel,
++ unsigned int offset)
++{
++ void __iomem *base = ddata->data->get_ch_base ?
++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs;
++
++ return readl(base + offset);
++}
++
++static inline void ocores_writel(struct ocores_pwm_device *ddata,
++ unsigned int channel,
++ unsigned int offset, u32 val)
++{
++ void __iomem *base = ddata->data->get_ch_base ?
++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs;
++
++ writel(val, base + offset);
++}
++
++static inline struct ocores_pwm_device *chip_to_ocores(struct pwm_chip *chip)
++{
++ return container_of(chip, struct ocores_pwm_device, chip);
++}
++
++static void __iomem *starfive_jh71x0_get_ch_base(void __iomem *base,
++ unsigned int channel)
++{
++ unsigned int offset = (channel > 3 ? 1 << 15 : 0) + (channel & 3) * 0x10;
++
++ return base + offset;
++}
++
++static int ocores_pwm_get_state(struct pwm_chip *chip,
++ struct pwm_device *pwm,
++ struct pwm_state *state)
++{
++ struct ocores_pwm_device *ddata = chip_to_ocores(chip);
++ u32 period_data, duty_data, ctrl_data;
++
++ period_data = ocores_readl(ddata, pwm->hwpwm, 0x8);
++ duty_data = ocores_readl(ddata, pwm->hwpwm, 0x4);
++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++
++ state->period = DIV_ROUND_UP_ULL((u64)period_data * NSEC_PER_SEC, ddata->clk_rate);
++ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty_data * NSEC_PER_SEC, ddata->clk_rate);
++ state->polarity = PWM_POLARITY_INVERSED;
++ state->enabled = (ctrl_data & REG_OCPWM_EN) ? true : false;
++
++ return 0;
++}
++
++static int ocores_pwm_apply(struct pwm_chip *chip,
++ struct pwm_device *pwm,
++ const struct pwm_state *state)
++{
++ struct ocores_pwm_device *ddata = chip_to_ocores(chip);
++ u32 ctrl_data = 0;
++ u64 period_data, duty_data;
++
++ if (state->polarity != PWM_POLARITY_INVERSED)
++ return -EINVAL;
++
++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0);
++
++ period_data = DIV_ROUND_DOWN_ULL(state->period * ddata->clk_rate, NSEC_PER_SEC);
++ if (period_data <= U32_MAX)
++ ocores_writel(ddata, pwm->hwpwm, 0x8, (u32)period_data);
++ else
++ return -EINVAL;
++
++ duty_data = DIV_ROUND_DOWN_ULL(state->duty_cycle * ddata->clk_rate, NSEC_PER_SEC);
++ if (duty_data <= U32_MAX)
++ ocores_writel(ddata, pwm->hwpwm, 0x4, (u32)duty_data);
++ else
++ return -EINVAL;
++
++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0);
++
++ if (state->enabled) {
++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++ ocores_writel(ddata, pwm->hwpwm, 0xC, ctrl_data | REG_OCPWM_EN | REG_OCPWM_OE);
++ }
++
++ return 0;
++}
++
++static const struct pwm_ops ocores_pwm_ops = {
++ .get_state = ocores_pwm_get_state,
++ .apply = ocores_pwm_apply,
++};
++
++static const struct ocores_pwm_data jh7100_pwm_data = {
++ .get_ch_base = starfive_jh71x0_get_ch_base,
++};
++
++static const struct ocores_pwm_data jh7110_pwm_data = {
++ .get_ch_base = starfive_jh71x0_get_ch_base,
++};
++
++static const struct of_device_id ocores_pwm_of_match[] = {
++ { .compatible = "opencores,pwm-v1" },
++ { .compatible = "starfive,jh7100-pwm", .data = &jh7100_pwm_data},
++ { .compatible = "starfive,jh7110-pwm", .data = &jh7110_pwm_data},
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ocores_pwm_of_match);
++
++static void ocores_reset_control_assert(void *data)
++{
++ reset_control_assert(data);
++}
++
++static int ocores_pwm_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *id;
++ struct device *dev = &pdev->dev;
++ struct ocores_pwm_device *ddata;
++ struct pwm_chip *chip;
++ int ret;
++
++ id = of_match_device(ocores_pwm_of_match, dev);
++ if (!id)
++ return -EINVAL;
++
++ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
++ if (!ddata)
++ return -ENOMEM;
++
++ ddata->data = id->data;
++ chip = &ddata->chip;
++ chip->dev = dev;
++ chip->ops = &ocores_pwm_ops;
++ chip->npwm = 8;
++ chip->of_pwm_n_cells = 3;
++
++ ddata->regs = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(ddata->regs))
++ return dev_err_probe(dev, PTR_ERR(ddata->regs),
++ "Unable to map IO resources\n");
++
++ ddata->clk = devm_clk_get_enabled(dev, NULL);
++ if (IS_ERR(ddata->clk))
++ return dev_err_probe(dev, PTR_ERR(ddata->clk),
++ "Unable to get pwm's clock\n");
++
++ ddata->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
++ if (IS_ERR(ddata->rst))
++ return dev_err_probe(dev, PTR_ERR(ddata->rst),
++ "Unable to get pwm's reset\n");
++
++ reset_control_deassert(ddata->rst);
++
++ ret = devm_add_action_or_reset(dev, ocores_reset_control_assert, ddata->rst);
++ if (ret)
++ return ret;
++
++ ddata->clk_rate = clk_get_rate(ddata->clk);
++ if (ddata->clk_rate <= 0)
++ return dev_err_probe(dev, ddata->clk_rate,
++ "Unable to get clock's rate\n");
++
++ ret = devm_pwmchip_add(dev, chip);
++ if (ret < 0)
++ return dev_err_probe(dev, ret, "Could not register PWM chip\n");
++
++ platform_set_drvdata(pdev, ddata);
++
++ return ret;
++}
++
++static struct platform_driver ocores_pwm_driver = {
++ .probe = ocores_pwm_probe,
++ .driver = {
++ .name = "ocores-pwm",
++ .of_match_table = ocores_pwm_of_match,
++ },
++};
++module_platform_driver(ocores_pwm_driver);
++
++MODULE_AUTHOR("Jieqin Chen");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("OpenCores PWM PTC driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 7d9521cad6474d45e9056176982e6da54d40bc19 Mon Sep 17 00:00:00 2001
+From: Eric Biggers <ebiggers@google.com>
+Date: Sun, 22 Oct 2023 01:10:42 -0700
+Subject: [PATCH 008/116] crypto: starfive - remove unnecessary alignmask for
+ ahashes
+
+The crypto API's support for alignmasks for ahash algorithms is nearly
+useless, as its only effect is to cause the API to align the key and
+result buffers. The drivers that happen to be specifying an alignmask
+for ahash rarely actually need it. When they do, it's easily fixable,
+especially considering that these buffers cannot be used for DMA.
+
+In preparation for removing alignmask support from ahash, this patch
+makes the starfive driver no longer use it. This driver did actually
+rely on it, but only for storing to the result buffer using int stores
+in starfive_hash_copy_hash(). This patch makes
+starfive_hash_copy_hash() use put_unaligned() instead. (It really
+should use a specific endianness, but that's an existing bug.)
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-hash.c | 13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-hash.c
++++ b/drivers/crypto/starfive/jh7110-hash.c
+@@ -209,7 +209,8 @@ static int starfive_hash_copy_hash(struc
+ data = (u32 *)req->result;
+
+ for (count = 0; count < mlen; count++)
+- data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR);
++ put_unaligned(readl(ctx->cryp->base + STARFIVE_HASH_SHARDR),
++ &data[count]);
+
+ return 0;
+ }
+@@ -628,7 +629,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -658,7 +658,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -687,7 +686,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -717,7 +715,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -746,7 +743,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -776,7 +772,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -805,7 +800,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -835,7 +829,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -864,7 +857,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SM3_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
+@@ -894,7 +886,6 @@ static struct ahash_engine_alg algs_sha2
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SM3_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ }
+ },
--- /dev/null
+From 52de0270ed6453727936b5a793dc367d75280e84 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Wed, 15 Nov 2023 01:12:13 +0800
+Subject: [PATCH 009/116] crypto: starfive - Update driver dependencies
+
+Change AMBA_PL08X to required dependency as the hash ops depends on it
+for data transfer.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/crypto/starfive/Kconfig
++++ b/drivers/crypto/starfive/Kconfig
+@@ -4,7 +4,7 @@
+
+ config CRYPTO_DEV_JH7110
+ tristate "StarFive JH7110 cryptographic engine driver"
+- depends on SOC_STARFIVE || AMBA_PL08X || COMPILE_TEST
++ depends on (SOC_STARFIVE && AMBA_PL08X) || COMPILE_TEST
+ depends on HAS_DMA
+ select CRYPTO_ENGINE
+ select CRYPTO_HMAC
--- /dev/null
+From 68a1bbb99455fd5ea80b7e21ec726f369abc9572 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Wed, 15 Nov 2023 01:12:14 +0800
+Subject: [PATCH 010/116] crypto: starfive - RSA poll csr for done status
+
+Hardware could not clear irq status without resetting the entire module.
+Driver receives irq immediately when mask bit is cleared causing
+intermittent errors in RSA calculations. Switch to use csr polling for
+done status instead.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-cryp.c | 8 -----
+ drivers/crypto/starfive/jh7110-cryp.h | 10 +++++-
+ drivers/crypto/starfive/jh7110-rsa.c | 49 +++++++--------------------
+ 3 files changed, 22 insertions(+), 45 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-cryp.c
++++ b/drivers/crypto/starfive/jh7110-cryp.c
+@@ -109,12 +109,6 @@ static irqreturn_t starfive_cryp_irq(int
+ tasklet_schedule(&cryp->hash_done);
+ }
+
+- if (status & STARFIVE_IE_FLAG_PKA_DONE) {
+- mask |= STARFIVE_IE_MASK_PKA_DONE;
+- writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET);
+- complete(&cryp->pka_done);
+- }
+-
+ return IRQ_HANDLED;
+ }
+
+@@ -159,8 +153,6 @@ static int starfive_cryp_probe(struct pl
+ return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst),
+ "Error getting hardware reset line\n");
+
+- init_completion(&cryp->pka_done);
+-
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+--- a/drivers/crypto/starfive/jh7110-cryp.h
++++ b/drivers/crypto/starfive/jh7110-cryp.h
+@@ -125,6 +125,15 @@ union starfive_pka_cacr {
+ };
+ };
+
++union starfive_pka_casr {
++ u32 v;
++ struct {
++#define STARFIVE_PKA_DONE BIT(0)
++ u32 done :1;
++ u32 rsvd_0 :31;
++ };
++};
++
+ struct starfive_rsa_key {
+ u8 *n;
+ u8 *e;
+@@ -183,7 +192,6 @@ struct starfive_cryp_dev {
+ struct crypto_engine *engine;
+ struct tasklet_struct aes_done;
+ struct tasklet_struct hash_done;
+- struct completion pka_done;
+ size_t assoclen;
+ size_t total_in;
+ size_t total_out;
+--- a/drivers/crypto/starfive/jh7110-rsa.c
++++ b/drivers/crypto/starfive/jh7110-rsa.c
+@@ -6,13 +6,7 @@
+ */
+
+ #include <linux/crypto.h>
+-#include <linux/delay.h>
+-#include <linux/device.h>
+-#include <linux/dma-direct.h>
+-#include <linux/interrupt.h>
+ #include <linux/iopoll.h>
+-#include <linux/io.h>
+-#include <linux/mod_devicetable.h>
+ #include <crypto/akcipher.h>
+ #include <crypto/algapi.h>
+ #include <crypto/internal/akcipher.h>
+@@ -28,13 +22,13 @@
+ #define STARFIVE_PKA_CAER_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x108)
+ #define STARFIVE_PKA_CANR_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x208)
+
+-// R^2 mod N and N0'
++/* R ^ 2 mod N and N0' */
+ #define CRYPTO_CMD_PRE 0x0
+-// A * R mod N ==> A
++/* A * R mod N ==> A */
+ #define CRYPTO_CMD_ARN 0x5
+-// A * E * R mod N ==> A
++/* A * E * R mod N ==> A */
+ #define CRYPTO_CMD_AERN 0x6
+-// A * A * R mod N ==> A
++/* A * A * R mod N ==> A */
+ #define CRYPTO_CMD_AARN 0x7
+
+ #define STARFIVE_RSA_MAX_KEYSZ 256
+@@ -43,21 +37,10 @@
+ static inline int starfive_pka_wait_done(struct starfive_cryp_ctx *ctx)
+ {
+ struct starfive_cryp_dev *cryp = ctx->cryp;
++ u32 status;
+
+- return wait_for_completion_timeout(&cryp->pka_done,
+- usecs_to_jiffies(100000));
+-}
+-
+-static inline void starfive_pka_irq_mask_clear(struct starfive_cryp_ctx *ctx)
+-{
+- struct starfive_cryp_dev *cryp = ctx->cryp;
+- u32 stat;
+-
+- stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET);
+- stat &= ~STARFIVE_IE_MASK_PKA_DONE;
+- writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET);
+-
+- reinit_completion(&cryp->pka_done);
++ return readl_relaxed_poll_timeout(cryp->base + STARFIVE_PKA_CASR_OFFSET, status,
++ status & STARFIVE_PKA_DONE, 10, 100000);
+ }
+
+ static void starfive_rsa_free_key(struct starfive_rsa_key *key)
+@@ -114,10 +97,9 @@ static int starfive_rsa_montgomery_form(
+ rctx->csr.pka.not_r2 = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ return -ETIMEDOUT;
+
+ for (loop = 0; loop <= opsize; loop++)
+@@ -136,10 +118,9 @@ static int starfive_rsa_montgomery_form(
+ rctx->csr.pka.start = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ return -ETIMEDOUT;
+ } else {
+ rctx->csr.pka.v = 0;
+@@ -151,10 +132,9 @@ static int starfive_rsa_montgomery_form(
+ rctx->csr.pka.pre_expf = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ return -ETIMEDOUT;
+
+ for (loop = 0; loop <= count; loop++)
+@@ -172,10 +152,9 @@ static int starfive_rsa_montgomery_form(
+ rctx->csr.pka.start = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ return -ETIMEDOUT;
+ }
+
+@@ -226,11 +205,10 @@ static int starfive_rsa_cpu_start(struct
+ rctx->csr.pka.start = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+ ret = -ETIMEDOUT;
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ goto rsa_err;
+
+ if (mlen) {
+@@ -242,10 +220,9 @@ static int starfive_rsa_cpu_start(struct
+ rctx->csr.pka.start = 1;
+ rctx->csr.pka.ie = 1;
+
+- starfive_pka_irq_mask_clear(ctx);
+ writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+
+- if (!starfive_pka_wait_done(ctx))
++ if (starfive_pka_wait_done(ctx))
+ goto rsa_err;
+ }
+ }
--- /dev/null
+From eea9f2c55cf944bbd5cdd43eb655416a867846af Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Mon, 20 Nov 2023 11:12:42 +0800
+Subject: [PATCH 011/116] crypto: starfive - Pad adata with zeroes
+
+Aad requires padding with zeroes up to 15 bytes in some cases. This
+patch increases the allocated buffer size for aad and prevents the
+driver accessing uninitialized memory region.
+
+v1->v2: Specify reason for alloc size change in descriptions.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-aes.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-aes.c
++++ b/drivers/crypto/starfive/jh7110-aes.c
+@@ -500,7 +500,7 @@ static int starfive_aes_prepare_req(stru
+ scatterwalk_start(&cryp->out_walk, rctx->out_sg);
+
+ if (cryp->assoclen) {
+- rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL);
++ rctx->adata = kzalloc(cryp->assoclen + AES_BLOCK_SIZE, GFP_KERNEL);
+ if (!rctx->adata)
+ return dev_err_probe(cryp->dev, -ENOMEM,
+ "Failed to alloc memory for adata");
+@@ -569,7 +569,7 @@ static int starfive_aes_aead_do_one_req(
+ struct starfive_cryp_ctx *ctx =
+ crypto_aead_ctx(crypto_aead_reqtfm(req));
+ struct starfive_cryp_dev *cryp = ctx->cryp;
+- struct starfive_cryp_request_ctx *rctx = ctx->rctx;
++ struct starfive_cryp_request_ctx *rctx;
+ u32 block[AES_BLOCK_32];
+ u32 stat;
+ int err;
+@@ -579,6 +579,8 @@ static int starfive_aes_aead_do_one_req(
+ if (err)
+ return err;
+
++ rctx = ctx->rctx;
++
+ if (!cryp->assoclen)
+ goto write_text;
+
--- /dev/null
+From 922b213ad22f93fb2788ce119084622ab3d25bf8 Mon Sep 17 00:00:00 2001
+From: Herbert Xu <herbert@gondor.apana.org.au>
+Date: Thu, 30 Nov 2023 18:12:55 +0800
+Subject: [PATCH 012/116] crypto: starfive - Remove cfb and ofb
+
+Remove the unused CFB/OFB implementation.
+
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-aes.c | 71 +--------------------------
+ drivers/crypto/starfive/jh7110-cryp.h | 2 -
+ 2 files changed, 1 insertion(+), 72 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-aes.c
++++ b/drivers/crypto/starfive/jh7110-aes.c
+@@ -262,12 +262,7 @@ static int starfive_aes_hw_init(struct s
+ rctx->csr.aes.mode = hw_mode;
+ rctx->csr.aes.cmode = !is_encrypt(cryp);
+ rctx->csr.aes.ie = 1;
+-
+- if (hw_mode == STARFIVE_AES_MODE_CFB ||
+- hw_mode == STARFIVE_AES_MODE_OFB)
+- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_128;
+- else
+- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1;
++ rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1;
+
+ if (cryp->side_chan) {
+ rctx->csr.aes.delay_aes = 1;
+@@ -294,8 +289,6 @@ static int starfive_aes_hw_init(struct s
+ starfive_aes_ccm_init(ctx);
+ starfive_aes_aead_hw_start(ctx, hw_mode);
+ break;
+- case STARFIVE_AES_MODE_OFB:
+- case STARFIVE_AES_MODE_CFB:
+ case STARFIVE_AES_MODE_CBC:
+ case STARFIVE_AES_MODE_CTR:
+ starfive_aes_write_iv(ctx, (void *)cryp->req.sreq->iv);
+@@ -785,26 +778,6 @@ static int starfive_aes_cbc_decrypt(stru
+ return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC);
+ }
+
+-static int starfive_aes_cfb_encrypt(struct skcipher_request *req)
+-{
+- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB | FLG_ENCRYPT);
+-}
+-
+-static int starfive_aes_cfb_decrypt(struct skcipher_request *req)
+-{
+- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB);
+-}
+-
+-static int starfive_aes_ofb_encrypt(struct skcipher_request *req)
+-{
+- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB | FLG_ENCRYPT);
+-}
+-
+-static int starfive_aes_ofb_decrypt(struct skcipher_request *req)
+-{
+- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB);
+-}
+-
+ static int starfive_aes_ctr_encrypt(struct skcipher_request *req)
+ {
+ return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR | FLG_ENCRYPT);
+@@ -903,48 +876,6 @@ static struct skcipher_engine_alg skciph
+ .cra_priority = 200,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 1,
+- .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 0xf,
+- .cra_module = THIS_MODULE,
+- },
+- .op = {
+- .do_one_request = starfive_aes_do_one_req,
+- },
+-}, {
+- .base.init = starfive_aes_init_tfm,
+- .base.setkey = starfive_aes_setkey,
+- .base.encrypt = starfive_aes_cfb_encrypt,
+- .base.decrypt = starfive_aes_cfb_decrypt,
+- .base.min_keysize = AES_MIN_KEY_SIZE,
+- .base.max_keysize = AES_MAX_KEY_SIZE,
+- .base.ivsize = AES_BLOCK_SIZE,
+- .base.base = {
+- .cra_name = "cfb(aes)",
+- .cra_driver_name = "starfive-cfb-aes",
+- .cra_priority = 200,
+- .cra_flags = CRYPTO_ALG_ASYNC,
+- .cra_blocksize = 1,
+- .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+- .cra_alignmask = 0xf,
+- .cra_module = THIS_MODULE,
+- },
+- .op = {
+- .do_one_request = starfive_aes_do_one_req,
+- },
+-}, {
+- .base.init = starfive_aes_init_tfm,
+- .base.setkey = starfive_aes_setkey,
+- .base.encrypt = starfive_aes_ofb_encrypt,
+- .base.decrypt = starfive_aes_ofb_decrypt,
+- .base.min_keysize = AES_MIN_KEY_SIZE,
+- .base.max_keysize = AES_MAX_KEY_SIZE,
+- .base.ivsize = AES_BLOCK_SIZE,
+- .base.base = {
+- .cra_name = "ofb(aes)",
+- .cra_driver_name = "starfive-ofb-aes",
+- .cra_priority = 200,
+- .cra_flags = CRYPTO_ALG_ASYNC,
+- .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct starfive_cryp_ctx),
+ .cra_alignmask = 0xf,
+ .cra_module = THIS_MODULE,
+--- a/drivers/crypto/starfive/jh7110-cryp.h
++++ b/drivers/crypto/starfive/jh7110-cryp.h
+@@ -50,8 +50,6 @@ union starfive_aes_csr {
+ u32 ccm_start :1;
+ #define STARFIVE_AES_MODE_ECB 0x0
+ #define STARFIVE_AES_MODE_CBC 0x1
+-#define STARFIVE_AES_MODE_CFB 0x2
+-#define STARFIVE_AES_MODE_OFB 0x3
+ #define STARFIVE_AES_MODE_CTR 0x4
+ #define STARFIVE_AES_MODE_CCM 0x5
+ #define STARFIVE_AES_MODE_GCM 0x6
--- /dev/null
+From 0dbdc763f10c5cfa968dffc290a7c060ee740172 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Mon, 4 Dec 2023 11:02:39 +0800
+Subject: [PATCH 013/116] crypto: starfive - Remove unneeded NULL checks
+
+NULL check before kfree_sensitive function is not needed.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311301702.LxswfETY-lkp@intel.com/
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-rsa.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-rsa.c
++++ b/drivers/crypto/starfive/jh7110-rsa.c
+@@ -45,12 +45,9 @@ static inline int starfive_pka_wait_done
+
+ static void starfive_rsa_free_key(struct starfive_rsa_key *key)
+ {
+- if (key->d)
+- kfree_sensitive(key->d);
+- if (key->e)
+- kfree_sensitive(key->e);
+- if (key->n)
+- kfree_sensitive(key->n);
++ kfree_sensitive(key->d);
++ kfree_sensitive(key->e);
++ kfree_sensitive(key->n);
+ memset(key, 0, sizeof(*key));
+ }
+
--- /dev/null
+From 708695ebf1a779de9a1fd2f72f7938afa6c14ada Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:51 +0800
+Subject: [PATCH 014/116] dt-bindings: PCI: Add PLDA XpressRICH PCIe host
+ common properties
+
+Add PLDA XpressRICH PCIe host common properties dt-binding doc.
+PolarFire PCIe host using PLDA IP. Move common properties from Microchip
+PolarFire PCIe host to PLDA files.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Tested-by: John Clark <inindev@gmail.com>
+---
+ .../bindings/pci/microchip,pcie-host.yaml | 55 +-------------
+ .../pci/plda,xpressrich3-axi-common.yaml | 75 +++++++++++++++++++
+ 2 files changed, 76 insertions(+), 54 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
+
+--- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
++++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
+@@ -10,21 +10,13 @@ maintainers:
+ - Daire McNamara <daire.mcnamara@microchip.com>
+
+ allOf:
+- - $ref: /schemas/pci/pci-bus.yaml#
++ - $ref: plda,xpressrich3-axi-common.yaml#
+ - $ref: /schemas/interrupt-controller/msi-controller.yaml#
+
+ properties:
+ compatible:
+ const: microchip,pcie-host-1.0 # PolarFire
+
+- reg:
+- maxItems: 2
+-
+- reg-names:
+- items:
+- - const: cfg
+- - const: apb
+-
+ clocks:
+ description:
+ Fabric Interface Controllers, FICs, are the interface between the FPGA
+@@ -52,18 +44,6 @@ properties:
+ items:
+ pattern: '^fic[0-3]$'
+
+- interrupts:
+- minItems: 1
+- items:
+- - description: PCIe host controller
+- - description: builtin MSI controller
+-
+- interrupt-names:
+- minItems: 1
+- items:
+- - const: pcie
+- - const: msi
+-
+ ranges:
+ maxItems: 1
+
+@@ -71,39 +51,6 @@ properties:
+ minItems: 1
+ maxItems: 6
+
+- msi-controller:
+- description: Identifies the node as an MSI controller.
+-
+- msi-parent:
+- description: MSI controller the device is capable of using.
+-
+- interrupt-controller:
+- type: object
+- properties:
+- '#address-cells':
+- const: 0
+-
+- '#interrupt-cells':
+- const: 1
+-
+- interrupt-controller: true
+-
+- required:
+- - '#address-cells'
+- - '#interrupt-cells'
+- - interrupt-controller
+-
+- additionalProperties: false
+-
+-required:
+- - reg
+- - reg-names
+- - "#interrupt-cells"
+- - interrupts
+- - interrupt-map-mask
+- - interrupt-map
+- - msi-controller
+-
+ unevaluatedProperties: false
+
+ examples:
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
+@@ -0,0 +1,75 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/plda,xpressrich3-axi-common.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: PLDA XpressRICH PCIe host common properties
++
++maintainers:
++ - Daire McNamara <daire.mcnamara@microchip.com>
++ - Kevin Xie <kevin.xie@starfivetech.com>
++
++description:
++ Generic PLDA XpressRICH PCIe host common properties.
++
++allOf:
++ - $ref: /schemas/pci/pci-bus.yaml#
++
++properties:
++ reg:
++ maxItems: 2
++
++ reg-names:
++ items:
++ - const: cfg
++ - const: apb
++
++ interrupts:
++ minItems: 1
++ items:
++ - description: PCIe host controller
++ - description: builtin MSI controller
++
++ interrupt-names:
++ minItems: 1
++ items:
++ - const: pcie
++ - const: msi
++
++ msi-controller:
++ description: Identifies the node as an MSI controller.
++
++ msi-parent:
++ description: MSI controller the device is capable of using.
++
++ interrupt-controller:
++ type: object
++ properties:
++ '#address-cells':
++ const: 0
++
++ '#interrupt-cells':
++ const: 1
++
++ interrupt-controller: true
++
++ required:
++ - '#address-cells'
++ - '#interrupt-cells'
++ - interrupt-controller
++
++ additionalProperties: false
++
++required:
++ - reg
++ - reg-names
++ - interrupts
++ - msi-controller
++ - "#interrupt-cells"
++ - interrupt-map-mask
++ - interrupt-map
++
++additionalProperties: true
++
++...
--- /dev/null
+From df67154aa92efdc774a8536ece6f431e7850aca2 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:52 +0800
+Subject: [PATCH 015/116] PCI: microchip: Move pcie-microchip-host.c to plda
+ directory
+
+For Microchip Polarfire PCIe host is PLDA XpressRich IP, move to plda
+directory. Prepare for refactoring the codes.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/Kconfig | 9 +--------
+ drivers/pci/controller/Makefile | 2 +-
+ drivers/pci/controller/plda/Kconfig | 14 ++++++++++++++
+ drivers/pci/controller/plda/Makefile | 2 ++
+ .../controller/{ => plda}/pcie-microchip-host.c | 2 +-
+ 5 files changed, 19 insertions(+), 10 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/Kconfig
+ create mode 100644 drivers/pci/controller/plda/Makefile
+ rename drivers/pci/controller/{ => plda}/pcie-microchip-host.c (99%)
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -215,14 +215,6 @@ config PCIE_MT7621
+ help
+ This selects a driver for the MediaTek MT7621 PCIe Controller.
+
+-config PCIE_MICROCHIP_HOST
+- tristate "Microchip AXI PCIe controller"
+- depends on PCI_MSI && OF
+- select PCI_HOST_COMMON
+- help
+- Say Y here if you want kernel to support the Microchip AXI PCIe
+- Host Bridge driver.
+-
+ config PCI_HYPERV_INTERFACE
+ tristate "Microsoft Hyper-V PCI Interface"
+ depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI
+@@ -345,4 +337,5 @@ config PCIE_XILINX_CPM
+ source "drivers/pci/controller/cadence/Kconfig"
+ source "drivers/pci/controller/dwc/Kconfig"
+ source "drivers/pci/controller/mobiveil/Kconfig"
++source "drivers/pci/controller/plda/Kconfig"
+ endmenu
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -32,7 +32,6 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-r
+ obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
+ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
+ obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o
+-obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+ obj-$(CONFIG_VMD) += vmd.o
+ obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
+ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
+@@ -43,6 +42,7 @@ obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621
+ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
+ obj-y += dwc/
+ obj-y += mobiveil/
++obj-y += plda/
+
+
+ # The following drivers are for devices that use the generic ACPI
+--- /dev/null
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -0,0 +1,14 @@
++# SPDX-License-Identifier: GPL-2.0
++
++menu "PLDA-based PCIe controllers"
++ depends on PCI
++
++config PCIE_MICROCHIP_HOST
++ tristate "Microchip AXI PCIe controller"
++ depends on PCI_MSI && OF
++ select PCI_HOST_COMMON
++ help
++ Say Y here if you want kernel to support the Microchip AXI PCIe
++ Host Bridge driver.
++
++endmenu
+--- /dev/null
++++ b/drivers/pci/controller/plda/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+--- a/drivers/pci/controller/pcie-microchip-host.c
++++ /dev/null
+@@ -1,1216 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * Microchip AXI PCIe Bridge host controller driver
+- *
+- * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
+- *
+- * Author: Daire McNamara <daire.mcnamara@microchip.com>
+- */
+-
+-#include <linux/bitfield.h>
+-#include <linux/clk.h>
+-#include <linux/irqchip/chained_irq.h>
+-#include <linux/irqdomain.h>
+-#include <linux/module.h>
+-#include <linux/msi.h>
+-#include <linux/of_address.h>
+-#include <linux/of_pci.h>
+-#include <linux/pci-ecam.h>
+-#include <linux/platform_device.h>
+-
+-#include "../pci.h"
+-
+-/* Number of MSI IRQs */
+-#define MC_MAX_NUM_MSI_IRQS 32
+-
+-/* PCIe Bridge Phy and Controller Phy offsets */
+-#define MC_PCIE1_BRIDGE_ADDR 0x00008000u
+-#define MC_PCIE1_CTRL_ADDR 0x0000a000u
+-
+-#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR)
+-#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR)
+-
+-/* PCIe Bridge Phy Regs */
+-#define PCIE_PCI_IRQ_DW0 0xa8
+-#define MSIX_CAP_MASK BIT(31)
+-#define NUM_MSI_MSGS_MASK GENMASK(6, 4)
+-#define NUM_MSI_MSGS_SHIFT 4
+-
+-#define IMASK_LOCAL 0x180
+-#define DMA_END_ENGINE_0_MASK 0x00000000u
+-#define DMA_END_ENGINE_0_SHIFT 0
+-#define DMA_END_ENGINE_1_MASK 0x00000000u
+-#define DMA_END_ENGINE_1_SHIFT 1
+-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u
+-#define DMA_ERROR_ENGINE_0_SHIFT 8
+-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u
+-#define DMA_ERROR_ENGINE_1_SHIFT 9
+-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u
+-#define A_ATR_EVT_POST_ERR_SHIFT 16
+-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u
+-#define A_ATR_EVT_FETCH_ERR_SHIFT 17
+-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u
+-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18
+-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u
+-#define A_ATR_EVT_DOORBELL_SHIFT 19
+-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u
+-#define P_ATR_EVT_POST_ERR_SHIFT 20
+-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u
+-#define P_ATR_EVT_FETCH_ERR_SHIFT 21
+-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u
+-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22
+-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u
+-#define P_ATR_EVT_DOORBELL_SHIFT 23
+-#define PM_MSI_INT_INTA_MASK 0x01000000u
+-#define PM_MSI_INT_INTA_SHIFT 24
+-#define PM_MSI_INT_INTB_MASK 0x02000000u
+-#define PM_MSI_INT_INTB_SHIFT 25
+-#define PM_MSI_INT_INTC_MASK 0x04000000u
+-#define PM_MSI_INT_INTC_SHIFT 26
+-#define PM_MSI_INT_INTD_MASK 0x08000000u
+-#define PM_MSI_INT_INTD_SHIFT 27
+-#define PM_MSI_INT_INTX_MASK 0x0f000000u
+-#define PM_MSI_INT_INTX_SHIFT 24
+-#define PM_MSI_INT_MSI_MASK 0x10000000u
+-#define PM_MSI_INT_MSI_SHIFT 28
+-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u
+-#define PM_MSI_INT_AER_EVT_SHIFT 29
+-#define PM_MSI_INT_EVENTS_MASK 0x40000000u
+-#define PM_MSI_INT_EVENTS_SHIFT 30
+-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u
+-#define PM_MSI_INT_SYS_ERR_SHIFT 31
+-#define NUM_LOCAL_EVENTS 15
+-#define ISTATUS_LOCAL 0x184
+-#define IMASK_HOST 0x188
+-#define ISTATUS_HOST 0x18c
+-#define IMSI_ADDR 0x190
+-#define ISTATUS_MSI 0x194
+-
+-/* PCIe Master table init defines */
+-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u
+-#define ATR0_PCIE_ATR_SIZE 0x25
+-#define ATR0_PCIE_ATR_SIZE_SHIFT 1
+-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu
+-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u
+-
+-/* PCIe AXI slave table init defines */
+-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u
+-#define ATR_SIZE_SHIFT 1
+-#define ATR_IMPL_ENABLE 1
+-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu
+-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u
+-#define PCIE_TX_RX_INTERFACE 0x00000000u
+-#define PCIE_CONFIG_INTERFACE 0x00000001u
+-
+-#define ATR_ENTRY_SIZE 32
+-
+-/* PCIe Controller Phy Regs */
+-#define SEC_ERROR_EVENT_CNT 0x20
+-#define DED_ERROR_EVENT_CNT 0x24
+-#define SEC_ERROR_INT 0x28
+-#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0)
+-#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4)
+-#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8)
+-#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12)
+-#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0)
+-#define NUM_SEC_ERROR_INTS (4)
+-#define SEC_ERROR_INT_MASK 0x2c
+-#define DED_ERROR_INT 0x30
+-#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0)
+-#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4)
+-#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8)
+-#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12)
+-#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0)
+-#define NUM_DED_ERROR_INTS (4)
+-#define DED_ERROR_INT_MASK 0x34
+-#define ECC_CONTROL 0x38
+-#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0)
+-#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1)
+-#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2)
+-#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3)
+-#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4)
+-#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5)
+-#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6)
+-#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7)
+-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8)
+-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9)
+-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10)
+-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11)
+-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12)
+-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13)
+-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14)
+-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15)
+-#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24)
+-#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25)
+-#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26)
+-#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27)
+-#define PCIE_EVENT_INT 0x14c
+-#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0)
+-#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1)
+-#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2)
+-#define PCIE_EVENT_INT_MASK GENMASK(2, 0)
+-#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16)
+-#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17)
+-#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18)
+-#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16)
+-#define PCIE_EVENT_INT_ENB_SHIFT 16
+-#define NUM_PCIE_EVENTS (3)
+-
+-/* PCIe Config space MSI capability structure */
+-#define MC_MSI_CAP_CTRL_OFFSET 0xe0u
+-
+-/* Events */
+-#define EVENT_PCIE_L2_EXIT 0
+-#define EVENT_PCIE_HOTRST_EXIT 1
+-#define EVENT_PCIE_DLUP_EXIT 2
+-#define EVENT_SEC_TX_RAM_SEC_ERR 3
+-#define EVENT_SEC_RX_RAM_SEC_ERR 4
+-#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5
+-#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6
+-#define EVENT_DED_TX_RAM_DED_ERR 7
+-#define EVENT_DED_RX_RAM_DED_ERR 8
+-#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9
+-#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10
+-#define EVENT_LOCAL_DMA_END_ENGINE_0 11
+-#define EVENT_LOCAL_DMA_END_ENGINE_1 12
+-#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13
+-#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14
+-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15
+-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16
+-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17
+-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18
+-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19
+-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20
+-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21
+-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22
+-#define EVENT_LOCAL_PM_MSI_INT_INTX 23
+-#define EVENT_LOCAL_PM_MSI_INT_MSI 24
+-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25
+-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26
+-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27
+-#define NUM_EVENTS 28
+-
+-#define PCIE_EVENT_CAUSE(x, s) \
+- [EVENT_PCIE_ ## x] = { __stringify(x), s }
+-
+-#define SEC_ERROR_CAUSE(x, s) \
+- [EVENT_SEC_ ## x] = { __stringify(x), s }
+-
+-#define DED_ERROR_CAUSE(x, s) \
+- [EVENT_DED_ ## x] = { __stringify(x), s }
+-
+-#define LOCAL_EVENT_CAUSE(x, s) \
+- [EVENT_LOCAL_ ## x] = { __stringify(x), s }
+-
+-#define PCIE_EVENT(x) \
+- .base = MC_PCIE_CTRL_ADDR, \
+- .offset = PCIE_EVENT_INT, \
+- .mask_offset = PCIE_EVENT_INT, \
+- .mask_high = 1, \
+- .mask = PCIE_EVENT_INT_ ## x ## _INT, \
+- .enb_mask = PCIE_EVENT_INT_ENB_MASK
+-
+-#define SEC_EVENT(x) \
+- .base = MC_PCIE_CTRL_ADDR, \
+- .offset = SEC_ERROR_INT, \
+- .mask_offset = SEC_ERROR_INT_MASK, \
+- .mask = SEC_ERROR_INT_ ## x ## _INT, \
+- .mask_high = 1, \
+- .enb_mask = 0
+-
+-#define DED_EVENT(x) \
+- .base = MC_PCIE_CTRL_ADDR, \
+- .offset = DED_ERROR_INT, \
+- .mask_offset = DED_ERROR_INT_MASK, \
+- .mask_high = 1, \
+- .mask = DED_ERROR_INT_ ## x ## _INT, \
+- .enb_mask = 0
+-
+-#define LOCAL_EVENT(x) \
+- .base = MC_PCIE_BRIDGE_ADDR, \
+- .offset = ISTATUS_LOCAL, \
+- .mask_offset = IMASK_LOCAL, \
+- .mask_high = 0, \
+- .mask = x ## _MASK, \
+- .enb_mask = 0
+-
+-#define PCIE_EVENT_TO_EVENT_MAP(x) \
+- { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
+-
+-#define SEC_ERROR_TO_EVENT_MAP(x) \
+- { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
+-
+-#define DED_ERROR_TO_EVENT_MAP(x) \
+- { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
+-
+-#define LOCAL_STATUS_TO_EVENT_MAP(x) \
+- { x ## _MASK, EVENT_LOCAL_ ## x }
+-
+-struct event_map {
+- u32 reg_mask;
+- u32 event_bit;
+-};
+-
+-struct mc_msi {
+- struct mutex lock; /* Protect used bitmap */
+- struct irq_domain *msi_domain;
+- struct irq_domain *dev_domain;
+- u32 num_vectors;
+- u64 vector_phy;
+- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
+-};
+-
+-struct mc_pcie {
+- void __iomem *axi_base_addr;
+- struct device *dev;
+- struct irq_domain *intx_domain;
+- struct irq_domain *event_domain;
+- raw_spinlock_t lock;
+- struct mc_msi msi;
+-};
+-
+-struct cause {
+- const char *sym;
+- const char *str;
+-};
+-
+-static const struct cause event_cause[NUM_EVENTS] = {
+- PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
+- PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
+- PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
+- SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"),
+- SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"),
+- SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"),
+- SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"),
+- DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"),
+- DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"),
+- DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"),
+- DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"),
+- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
+- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
+- LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
+- LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
+- LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
+- LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
+- LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
+- LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
+- LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
+- LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
+- LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
+-};
+-
+-static struct event_map pcie_event_to_event[] = {
+- PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
+- PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
+- PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT),
+-};
+-
+-static struct event_map sec_error_to_event[] = {
+- SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
+- SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
+- SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
+- SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR),
+-};
+-
+-static struct event_map ded_error_to_event[] = {
+- DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
+- DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
+- DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
+- DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR),
+-};
+-
+-static struct event_map local_status_to_event[] = {
+- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
+- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
+- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
+- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
+- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
+- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
+- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
+- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
+- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
+- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
+- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
+- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
+-};
+-
+-static struct {
+- u32 base;
+- u32 offset;
+- u32 mask;
+- u32 shift;
+- u32 enb_mask;
+- u32 mask_high;
+- u32 mask_offset;
+-} event_descs[] = {
+- { PCIE_EVENT(L2_EXIT) },
+- { PCIE_EVENT(HOTRST_EXIT) },
+- { PCIE_EVENT(DLUP_EXIT) },
+- { SEC_EVENT(TX_RAM_SEC_ERR) },
+- { SEC_EVENT(RX_RAM_SEC_ERR) },
+- { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
+- { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
+- { DED_EVENT(TX_RAM_DED_ERR) },
+- { DED_EVENT(RX_RAM_DED_ERR) },
+- { DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
+- { DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
+- { LOCAL_EVENT(DMA_END_ENGINE_0) },
+- { LOCAL_EVENT(DMA_END_ENGINE_1) },
+- { LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
+- { LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
+- { LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
+- { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
+- { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
+- { LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
+- { LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
+- { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
+- { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
+- { LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
+- { LOCAL_EVENT(PM_MSI_INT_INTX) },
+- { LOCAL_EVENT(PM_MSI_INT_MSI) },
+- { LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
+- { LOCAL_EVENT(PM_MSI_INT_EVENTS) },
+- { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) },
+-};
+-
+-static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
+-
+-static struct mc_pcie *port;
+-
+-static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
+-{
+- struct mc_msi *msi = &port->msi;
+- u16 reg;
+- u8 queue_size;
+-
+- /* Fixup MSI enable flag */
+- reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+- reg |= PCI_MSI_FLAGS_ENABLE;
+- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+-
+- /* Fixup PCI MSI queue flags */
+- queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg);
+- reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size);
+- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+-
+- /* Fixup MSI addr fields */
+- writel_relaxed(lower_32_bits(msi->vector_phy),
+- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO);
+- writel_relaxed(upper_32_bits(msi->vector_phy),
+- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+-}
+-
+-static void mc_handle_msi(struct irq_desc *desc)
+-{
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+- struct device *dev = port->dev;
+- struct mc_msi *msi = &port->msi;
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- unsigned long status;
+- u32 bit;
+- int ret;
+-
+- chained_irq_enter(chip, desc);
+-
+- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+- if (status & PM_MSI_INT_MSI_MASK) {
+- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
+- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+- for_each_set_bit(bit, &status, msi->num_vectors) {
+- ret = generic_handle_domain_irq(msi->dev_domain, bit);
+- if (ret)
+- dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
+- bit);
+- }
+- }
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_msi_bottom_irq_ack(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- u32 bitpos = data->hwirq;
+-
+- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+-}
+-
+-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- phys_addr_t addr = port->msi.vector_phy;
+-
+- msg->address_lo = lower_32_bits(addr);
+- msg->address_hi = upper_32_bits(addr);
+- msg->data = data->hwirq;
+-
+- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
+- (int)data->hwirq, msg->address_hi, msg->address_lo);
+-}
+-
+-static int mc_msi_set_affinity(struct irq_data *irq_data,
+- const struct cpumask *mask, bool force)
+-{
+- return -EINVAL;
+-}
+-
+-static struct irq_chip mc_msi_bottom_irq_chip = {
+- .name = "Microchip MSI",
+- .irq_ack = mc_msi_bottom_irq_ack,
+- .irq_compose_msi_msg = mc_compose_msi_msg,
+- .irq_set_affinity = mc_msi_set_affinity,
+-};
+-
+-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+- unsigned int nr_irqs, void *args)
+-{
+- struct mc_pcie *port = domain->host_data;
+- struct mc_msi *msi = &port->msi;
+- unsigned long bit;
+-
+- mutex_lock(&msi->lock);
+- bit = find_first_zero_bit(msi->used, msi->num_vectors);
+- if (bit >= msi->num_vectors) {
+- mutex_unlock(&msi->lock);
+- return -ENOSPC;
+- }
+-
+- set_bit(bit, msi->used);
+-
+- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
+- domain->host_data, handle_edge_irq, NULL, NULL);
+-
+- mutex_unlock(&msi->lock);
+-
+- return 0;
+-}
+-
+-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+- unsigned int nr_irqs)
+-{
+- struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+- struct mc_pcie *port = irq_data_get_irq_chip_data(d);
+- struct mc_msi *msi = &port->msi;
+-
+- mutex_lock(&msi->lock);
+-
+- if (test_bit(d->hwirq, msi->used))
+- __clear_bit(d->hwirq, msi->used);
+- else
+- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
+-
+- mutex_unlock(&msi->lock);
+-}
+-
+-static const struct irq_domain_ops msi_domain_ops = {
+- .alloc = mc_irq_msi_domain_alloc,
+- .free = mc_irq_msi_domain_free,
+-};
+-
+-static struct irq_chip mc_msi_irq_chip = {
+- .name = "Microchip PCIe MSI",
+- .irq_ack = irq_chip_ack_parent,
+- .irq_mask = pci_msi_mask_irq,
+- .irq_unmask = pci_msi_unmask_irq,
+-};
+-
+-static struct msi_domain_info mc_msi_domain_info = {
+- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+- MSI_FLAG_PCI_MSIX),
+- .chip = &mc_msi_irq_chip,
+-};
+-
+-static int mc_allocate_msi_domains(struct mc_pcie *port)
+-{
+- struct device *dev = port->dev;
+- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+- struct mc_msi *msi = &port->msi;
+-
+- mutex_init(&port->msi.lock);
+-
+- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
+- &msi_domain_ops, port);
+- if (!msi->dev_domain) {
+- dev_err(dev, "failed to create IRQ domain\n");
+- return -ENOMEM;
+- }
+-
+- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
+- msi->dev_domain);
+- if (!msi->msi_domain) {
+- dev_err(dev, "failed to create MSI domain\n");
+- irq_domain_remove(msi->dev_domain);
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-static void mc_handle_intx(struct irq_desc *desc)
+-{
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+- struct device *dev = port->dev;
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- unsigned long status;
+- u32 bit;
+- int ret;
+-
+- chained_irq_enter(chip, desc);
+-
+- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+- if (status & PM_MSI_INT_INTX_MASK) {
+- status &= PM_MSI_INT_INTX_MASK;
+- status >>= PM_MSI_INT_INTX_SHIFT;
+- for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+- ret = generic_handle_domain_irq(port->intx_domain, bit);
+- if (ret)
+- dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
+- bit);
+- }
+- }
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_ack_intx_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-
+- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+-}
+-
+-static void mc_mask_intx_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- unsigned long flags;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+- u32 val;
+-
+- raw_spin_lock_irqsave(&port->lock, flags);
+- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+- val &= ~mask;
+- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+- raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static void mc_unmask_intx_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- unsigned long flags;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+- u32 val;
+-
+- raw_spin_lock_irqsave(&port->lock, flags);
+- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+- val |= mask;
+- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+- raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static struct irq_chip mc_intx_irq_chip = {
+- .name = "Microchip PCIe INTx",
+- .irq_ack = mc_ack_intx_irq,
+- .irq_mask = mc_mask_intx_irq,
+- .irq_unmask = mc_unmask_intx_irq,
+-};
+-
+-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
+-{
+- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
+- irq_set_chip_data(irq, domain->host_data);
+-
+- return 0;
+-}
+-
+-static const struct irq_domain_ops intx_domain_ops = {
+- .map = mc_pcie_intx_map,
+-};
+-
+-static inline u32 reg_to_event(u32 reg, struct event_map field)
+-{
+- return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
+-}
+-
+-static u32 pcie_events(struct mc_pcie *port)
+-{
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+- u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT);
+- u32 val = 0;
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
+- val |= reg_to_event(reg, pcie_event_to_event[i]);
+-
+- return val;
+-}
+-
+-static u32 sec_errors(struct mc_pcie *port)
+-{
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+- u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT);
+- u32 val = 0;
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
+- val |= reg_to_event(reg, sec_error_to_event[i]);
+-
+- return val;
+-}
+-
+-static u32 ded_errors(struct mc_pcie *port)
+-{
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+- u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT);
+- u32 val = 0;
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
+- val |= reg_to_event(reg, ded_error_to_event[i]);
+-
+- return val;
+-}
+-
+-static u32 local_events(struct mc_pcie *port)
+-{
+- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+- u32 val = 0;
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
+- val |= reg_to_event(reg, local_status_to_event[i]);
+-
+- return val;
+-}
+-
+-static u32 get_events(struct mc_pcie *port)
+-{
+- u32 events = 0;
+-
+- events |= pcie_events(port);
+- events |= sec_errors(port);
+- events |= ded_errors(port);
+- events |= local_events(port);
+-
+- return events;
+-}
+-
+-static irqreturn_t mc_event_handler(int irq, void *dev_id)
+-{
+- struct mc_pcie *port = dev_id;
+- struct device *dev = port->dev;
+- struct irq_data *data;
+-
+- data = irq_domain_get_irq_data(port->event_domain, irq);
+-
+- if (event_cause[data->hwirq].str)
+- dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
+- else
+- dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
+-
+- return IRQ_HANDLED;
+-}
+-
+-static void mc_handle_event(struct irq_desc *desc)
+-{
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
+- unsigned long events;
+- u32 bit;
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+-
+- chained_irq_enter(chip, desc);
+-
+- events = get_events(port);
+-
+- for_each_set_bit(bit, &events, NUM_EVENTS)
+- generic_handle_domain_irq(port->event_domain, bit);
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_ack_event_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- u32 event = data->hwirq;
+- void __iomem *addr;
+- u32 mask;
+-
+- addr = port->axi_base_addr + event_descs[event].base +
+- event_descs[event].offset;
+- mask = event_descs[event].mask;
+- mask |= event_descs[event].enb_mask;
+-
+- writel_relaxed(mask, addr);
+-}
+-
+-static void mc_mask_event_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- u32 event = data->hwirq;
+- void __iomem *addr;
+- u32 mask;
+- u32 val;
+-
+- addr = port->axi_base_addr + event_descs[event].base +
+- event_descs[event].mask_offset;
+- mask = event_descs[event].mask;
+- if (event_descs[event].enb_mask) {
+- mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+- mask &= PCIE_EVENT_INT_ENB_MASK;
+- }
+-
+- if (!event_descs[event].mask_high)
+- mask = ~mask;
+-
+- raw_spin_lock(&port->lock);
+- val = readl_relaxed(addr);
+- if (event_descs[event].mask_high)
+- val |= mask;
+- else
+- val &= mask;
+-
+- writel_relaxed(val, addr);
+- raw_spin_unlock(&port->lock);
+-}
+-
+-static void mc_unmask_event_irq(struct irq_data *data)
+-{
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- u32 event = data->hwirq;
+- void __iomem *addr;
+- u32 mask;
+- u32 val;
+-
+- addr = port->axi_base_addr + event_descs[event].base +
+- event_descs[event].mask_offset;
+- mask = event_descs[event].mask;
+-
+- if (event_descs[event].enb_mask)
+- mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+-
+- if (event_descs[event].mask_high)
+- mask = ~mask;
+-
+- if (event_descs[event].enb_mask)
+- mask &= PCIE_EVENT_INT_ENB_MASK;
+-
+- raw_spin_lock(&port->lock);
+- val = readl_relaxed(addr);
+- if (event_descs[event].mask_high)
+- val &= mask;
+- else
+- val |= mask;
+- writel_relaxed(val, addr);
+- raw_spin_unlock(&port->lock);
+-}
+-
+-static struct irq_chip mc_event_irq_chip = {
+- .name = "Microchip PCIe EVENT",
+- .irq_ack = mc_ack_event_irq,
+- .irq_mask = mc_mask_event_irq,
+- .irq_unmask = mc_unmask_event_irq,
+-};
+-
+-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
+-{
+- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
+- irq_set_chip_data(irq, domain->host_data);
+-
+- return 0;
+-}
+-
+-static const struct irq_domain_ops event_domain_ops = {
+- .map = mc_pcie_event_map,
+-};
+-
+-static inline void mc_pcie_deinit_clk(void *data)
+-{
+- struct clk *clk = data;
+-
+- clk_disable_unprepare(clk);
+-}
+-
+-static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
+-{
+- struct clk *clk;
+- int ret;
+-
+- clk = devm_clk_get_optional(dev, id);
+- if (IS_ERR(clk))
+- return clk;
+- if (!clk)
+- return clk;
+-
+- ret = clk_prepare_enable(clk);
+- if (ret)
+- return ERR_PTR(ret);
+-
+- devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk);
+-
+- return clk;
+-}
+-
+-static int mc_pcie_init_clks(struct device *dev)
+-{
+- int i;
+- struct clk *fic;
+-
+- /*
+- * PCIe may be clocked via Fabric Interface using between 1 and 4
+- * clocks. Scan DT for clocks and enable them if present
+- */
+- for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
+- fic = mc_pcie_init_clk(dev, poss_clks[i]);
+- if (IS_ERR(fic))
+- return PTR_ERR(fic);
+- }
+-
+- return 0;
+-}
+-
+-static int mc_pcie_init_irq_domains(struct mc_pcie *port)
+-{
+- struct device *dev = port->dev;
+- struct device_node *node = dev->of_node;
+- struct device_node *pcie_intc_node;
+-
+- /* Setup INTx */
+- pcie_intc_node = of_get_next_child(node, NULL);
+- if (!pcie_intc_node) {
+- dev_err(dev, "failed to find PCIe Intc node\n");
+- return -EINVAL;
+- }
+-
+- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
+- &event_domain_ops, port);
+- if (!port->event_domain) {
+- dev_err(dev, "failed to get event domain\n");
+- of_node_put(pcie_intc_node);
+- return -ENOMEM;
+- }
+-
+- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
+-
+- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+- &intx_domain_ops, port);
+- if (!port->intx_domain) {
+- dev_err(dev, "failed to get an INTx IRQ domain\n");
+- of_node_put(pcie_intc_node);
+- return -ENOMEM;
+- }
+-
+- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
+-
+- of_node_put(pcie_intc_node);
+- raw_spin_lock_init(&port->lock);
+-
+- return mc_allocate_msi_domains(port);
+-}
+-
+-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+- phys_addr_t axi_addr, phys_addr_t pci_addr,
+- size_t size)
+-{
+- u32 atr_sz = ilog2(size) - 1;
+- u32 val;
+-
+- if (index == 0)
+- val = PCIE_CONFIG_INTERFACE;
+- else
+- val = PCIE_TX_RX_INTERFACE;
+-
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_PARAM);
+-
+- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
+- ATR_IMPL_ENABLE;
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_SRCADDR_PARAM);
+-
+- val = upper_32_bits(axi_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_SRC_ADDR);
+-
+- val = lower_32_bits(pci_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
+-
+- val = upper_32_bits(pci_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
+-
+- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+-}
+-
+-static int mc_pcie_setup_windows(struct platform_device *pdev,
+- struct mc_pcie *port)
+-{
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+- struct resource_entry *entry;
+- u64 pci_addr;
+- u32 index = 1;
+-
+- resource_list_for_each_entry(entry, &bridge->windows) {
+- if (resource_type(entry->res) == IORESOURCE_MEM) {
+- pci_addr = entry->res->start - entry->offset;
+- mc_pcie_setup_window(bridge_base_addr, index,
+- entry->res->start, pci_addr,
+- resource_size(entry->res));
+- index++;
+- }
+- }
+-
+- return 0;
+-}
+-
+-static inline void mc_clear_secs(struct mc_pcie *port)
+-{
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-
+- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
+- SEC_ERROR_INT);
+- writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT);
+-}
+-
+-static inline void mc_clear_deds(struct mc_pcie *port)
+-{
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-
+- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
+- DED_ERROR_INT);
+- writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT);
+-}
+-
+-static void mc_disable_interrupts(struct mc_pcie *port)
+-{
+- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+- u32 val;
+-
+- /* Ensure ECC bypass is enabled */
+- val = ECC_CONTROL_TX_RAM_ECC_BYPASS |
+- ECC_CONTROL_RX_RAM_ECC_BYPASS |
+- ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS |
+- ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS;
+- writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
+-
+- /* Disable SEC errors and clear any outstanding */
+- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
+- SEC_ERROR_INT_MASK);
+- mc_clear_secs(port);
+-
+- /* Disable DED errors and clear any outstanding */
+- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
+- DED_ERROR_INT_MASK);
+- mc_clear_deds(port);
+-
+- /* Disable local interrupts and clear any outstanding */
+- writel_relaxed(0, bridge_base_addr + IMASK_LOCAL);
+- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL);
+- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI);
+-
+- /* Disable PCIe events and clear any outstanding */
+- val = PCIE_EVENT_INT_L2_EXIT_INT |
+- PCIE_EVENT_INT_HOTRST_EXIT_INT |
+- PCIE_EVENT_INT_DLUP_EXIT_INT |
+- PCIE_EVENT_INT_L2_EXIT_INT_MASK |
+- PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK |
+- PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
+- writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
+-
+- /* Disable host interrupts and clear any outstanding */
+- writel_relaxed(0, bridge_base_addr + IMASK_HOST);
+- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+-}
+-
+-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
+-{
+- struct device *dev = &pdev->dev;
+- int irq;
+- int i, intx_irq, msi_irq, event_irq;
+- int ret;
+-
+- ret = mc_pcie_init_irq_domains(port);
+- if (ret) {
+- dev_err(dev, "failed creating IRQ domains\n");
+- return ret;
+- }
+-
+- irq = platform_get_irq(pdev, 0);
+- if (irq < 0)
+- return -ENODEV;
+-
+- for (i = 0; i < NUM_EVENTS; i++) {
+- event_irq = irq_create_mapping(port->event_domain, i);
+- if (!event_irq) {
+- dev_err(dev, "failed to map hwirq %d\n", i);
+- return -ENXIO;
+- }
+-
+- ret = devm_request_irq(dev, event_irq, mc_event_handler,
+- 0, event_cause[i].sym, port);
+- if (ret) {
+- dev_err(dev, "failed to request IRQ %d\n", event_irq);
+- return ret;
+- }
+- }
+-
+- intx_irq = irq_create_mapping(port->event_domain,
+- EVENT_LOCAL_PM_MSI_INT_INTX);
+- if (!intx_irq) {
+- dev_err(dev, "failed to map INTx interrupt\n");
+- return -ENXIO;
+- }
+-
+- /* Plug the INTx chained handler */
+- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
+-
+- msi_irq = irq_create_mapping(port->event_domain,
+- EVENT_LOCAL_PM_MSI_INT_MSI);
+- if (!msi_irq)
+- return -ENXIO;
+-
+- /* Plug the MSI chained handler */
+- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
+-
+- /* Plug the main event chained handler */
+- irq_set_chained_handler_and_data(irq, mc_handle_event, port);
+-
+- return 0;
+-}
+-
+-static int mc_platform_init(struct pci_config_window *cfg)
+-{
+- struct device *dev = cfg->parent;
+- struct platform_device *pdev = to_platform_device(dev);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- int ret;
+-
+- /* Configure address translation table 0 for PCIe config space */
+- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
+- cfg->res.start,
+- resource_size(&cfg->res));
+-
+- /* Need some fixups in config space */
+- mc_pcie_enable_msi(port, cfg->win);
+-
+- /* Configure non-config space outbound ranges */
+- ret = mc_pcie_setup_windows(pdev, port);
+- if (ret)
+- return ret;
+-
+- /* Address translation is up; safe to enable interrupts */
+- ret = mc_init_interrupts(pdev, port);
+- if (ret)
+- return ret;
+-
+- return 0;
+-}
+-
+-static int mc_host_probe(struct platform_device *pdev)
+-{
+- struct device *dev = &pdev->dev;
+- void __iomem *bridge_base_addr;
+- int ret;
+- u32 val;
+-
+- port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+- if (!port)
+- return -ENOMEM;
+-
+- port->dev = dev;
+-
+- port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
+- if (IS_ERR(port->axi_base_addr))
+- return PTR_ERR(port->axi_base_addr);
+-
+- mc_disable_interrupts(port);
+-
+- bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-
+- /* Allow enabling MSI by disabling MSI-X */
+- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+- val &= ~MSIX_CAP_MASK;
+- writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0);
+-
+- /* Pick num vectors from bitfile programmed onto FPGA fabric */
+- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+- val &= NUM_MSI_MSGS_MASK;
+- val >>= NUM_MSI_MSGS_SHIFT;
+-
+- port->msi.num_vectors = 1 << val;
+-
+- /* Pick vector address from design */
+- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
+-
+- ret = mc_pcie_init_clks(dev);
+- if (ret) {
+- dev_err(dev, "failed to get clock resources, error %d\n", ret);
+- return -ENODEV;
+- }
+-
+- return pci_host_common_probe(pdev);
+-}
+-
+-static const struct pci_ecam_ops mc_ecam_ops = {
+- .init = mc_platform_init,
+- .pci_ops = {
+- .map_bus = pci_ecam_map_bus,
+- .read = pci_generic_config_read,
+- .write = pci_generic_config_write,
+- }
+-};
+-
+-static const struct of_device_id mc_pcie_of_match[] = {
+- {
+- .compatible = "microchip,pcie-host-1.0",
+- .data = &mc_ecam_ops,
+- },
+- {},
+-};
+-
+-MODULE_DEVICE_TABLE(of, mc_pcie_of_match);
+-
+-static struct platform_driver mc_pcie_driver = {
+- .probe = mc_host_probe,
+- .driver = {
+- .name = "microchip-pcie",
+- .of_match_table = mc_pcie_of_match,
+- .suppress_bind_attrs = true,
+- },
+-};
+-
+-builtin_platform_driver(mc_pcie_driver);
+-MODULE_LICENSE("GPL");
+-MODULE_DESCRIPTION("Microchip PCIe host controller driver");
+-MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -0,0 +1,1216 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Microchip AXI PCIe Bridge host controller driver
++ *
++ * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
++ *
++ * Author: Daire McNamara <daire.mcnamara@microchip.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_address.h>
++#include <linux/of_pci.h>
++#include <linux/pci-ecam.h>
++#include <linux/platform_device.h>
++
++#include "../../pci.h"
++
++/* Number of MSI IRQs */
++#define MC_MAX_NUM_MSI_IRQS 32
++
++/* PCIe Bridge Phy and Controller Phy offsets */
++#define MC_PCIE1_BRIDGE_ADDR 0x00008000u
++#define MC_PCIE1_CTRL_ADDR 0x0000a000u
++
++#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR)
++#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR)
++
++/* PCIe Bridge Phy Regs */
++#define PCIE_PCI_IRQ_DW0 0xa8
++#define MSIX_CAP_MASK BIT(31)
++#define NUM_MSI_MSGS_MASK GENMASK(6, 4)
++#define NUM_MSI_MSGS_SHIFT 4
++
++#define IMASK_LOCAL 0x180
++#define DMA_END_ENGINE_0_MASK 0x00000000u
++#define DMA_END_ENGINE_0_SHIFT 0
++#define DMA_END_ENGINE_1_MASK 0x00000000u
++#define DMA_END_ENGINE_1_SHIFT 1
++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u
++#define DMA_ERROR_ENGINE_0_SHIFT 8
++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u
++#define DMA_ERROR_ENGINE_1_SHIFT 9
++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u
++#define A_ATR_EVT_POST_ERR_SHIFT 16
++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u
++#define A_ATR_EVT_FETCH_ERR_SHIFT 17
++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u
++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18
++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u
++#define A_ATR_EVT_DOORBELL_SHIFT 19
++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u
++#define P_ATR_EVT_POST_ERR_SHIFT 20
++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u
++#define P_ATR_EVT_FETCH_ERR_SHIFT 21
++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u
++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22
++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u
++#define P_ATR_EVT_DOORBELL_SHIFT 23
++#define PM_MSI_INT_INTA_MASK 0x01000000u
++#define PM_MSI_INT_INTA_SHIFT 24
++#define PM_MSI_INT_INTB_MASK 0x02000000u
++#define PM_MSI_INT_INTB_SHIFT 25
++#define PM_MSI_INT_INTC_MASK 0x04000000u
++#define PM_MSI_INT_INTC_SHIFT 26
++#define PM_MSI_INT_INTD_MASK 0x08000000u
++#define PM_MSI_INT_INTD_SHIFT 27
++#define PM_MSI_INT_INTX_MASK 0x0f000000u
++#define PM_MSI_INT_INTX_SHIFT 24
++#define PM_MSI_INT_MSI_MASK 0x10000000u
++#define PM_MSI_INT_MSI_SHIFT 28
++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u
++#define PM_MSI_INT_AER_EVT_SHIFT 29
++#define PM_MSI_INT_EVENTS_MASK 0x40000000u
++#define PM_MSI_INT_EVENTS_SHIFT 30
++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u
++#define PM_MSI_INT_SYS_ERR_SHIFT 31
++#define NUM_LOCAL_EVENTS 15
++#define ISTATUS_LOCAL 0x184
++#define IMASK_HOST 0x188
++#define ISTATUS_HOST 0x18c
++#define IMSI_ADDR 0x190
++#define ISTATUS_MSI 0x194
++
++/* PCIe Master table init defines */
++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u
++#define ATR0_PCIE_ATR_SIZE 0x25
++#define ATR0_PCIE_ATR_SIZE_SHIFT 1
++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu
++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u
++
++/* PCIe AXI slave table init defines */
++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u
++#define ATR_SIZE_SHIFT 1
++#define ATR_IMPL_ENABLE 1
++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu
++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u
++#define PCIE_TX_RX_INTERFACE 0x00000000u
++#define PCIE_CONFIG_INTERFACE 0x00000001u
++
++#define ATR_ENTRY_SIZE 32
++
++/* PCIe Controller Phy Regs */
++#define SEC_ERROR_EVENT_CNT 0x20
++#define DED_ERROR_EVENT_CNT 0x24
++#define SEC_ERROR_INT 0x28
++#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0)
++#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4)
++#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8)
++#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12)
++#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0)
++#define NUM_SEC_ERROR_INTS (4)
++#define SEC_ERROR_INT_MASK 0x2c
++#define DED_ERROR_INT 0x30
++#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0)
++#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4)
++#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8)
++#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12)
++#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0)
++#define NUM_DED_ERROR_INTS (4)
++#define DED_ERROR_INT_MASK 0x34
++#define ECC_CONTROL 0x38
++#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0)
++#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1)
++#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2)
++#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3)
++#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4)
++#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5)
++#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6)
++#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7)
++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8)
++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9)
++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10)
++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11)
++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12)
++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13)
++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14)
++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15)
++#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24)
++#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25)
++#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26)
++#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27)
++#define PCIE_EVENT_INT 0x14c
++#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0)
++#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1)
++#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2)
++#define PCIE_EVENT_INT_MASK GENMASK(2, 0)
++#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16)
++#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17)
++#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18)
++#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16)
++#define PCIE_EVENT_INT_ENB_SHIFT 16
++#define NUM_PCIE_EVENTS (3)
++
++/* PCIe Config space MSI capability structure */
++#define MC_MSI_CAP_CTRL_OFFSET 0xe0u
++
++/* Events */
++#define EVENT_PCIE_L2_EXIT 0
++#define EVENT_PCIE_HOTRST_EXIT 1
++#define EVENT_PCIE_DLUP_EXIT 2
++#define EVENT_SEC_TX_RAM_SEC_ERR 3
++#define EVENT_SEC_RX_RAM_SEC_ERR 4
++#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5
++#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6
++#define EVENT_DED_TX_RAM_DED_ERR 7
++#define EVENT_DED_RX_RAM_DED_ERR 8
++#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9
++#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10
++#define EVENT_LOCAL_DMA_END_ENGINE_0 11
++#define EVENT_LOCAL_DMA_END_ENGINE_1 12
++#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13
++#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14
++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15
++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16
++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17
++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18
++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19
++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20
++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21
++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22
++#define EVENT_LOCAL_PM_MSI_INT_INTX 23
++#define EVENT_LOCAL_PM_MSI_INT_MSI 24
++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25
++#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26
++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27
++#define NUM_EVENTS 28
++
++#define PCIE_EVENT_CAUSE(x, s) \
++ [EVENT_PCIE_ ## x] = { __stringify(x), s }
++
++#define SEC_ERROR_CAUSE(x, s) \
++ [EVENT_SEC_ ## x] = { __stringify(x), s }
++
++#define DED_ERROR_CAUSE(x, s) \
++ [EVENT_DED_ ## x] = { __stringify(x), s }
++
++#define LOCAL_EVENT_CAUSE(x, s) \
++ [EVENT_LOCAL_ ## x] = { __stringify(x), s }
++
++#define PCIE_EVENT(x) \
++ .base = MC_PCIE_CTRL_ADDR, \
++ .offset = PCIE_EVENT_INT, \
++ .mask_offset = PCIE_EVENT_INT, \
++ .mask_high = 1, \
++ .mask = PCIE_EVENT_INT_ ## x ## _INT, \
++ .enb_mask = PCIE_EVENT_INT_ENB_MASK
++
++#define SEC_EVENT(x) \
++ .base = MC_PCIE_CTRL_ADDR, \
++ .offset = SEC_ERROR_INT, \
++ .mask_offset = SEC_ERROR_INT_MASK, \
++ .mask = SEC_ERROR_INT_ ## x ## _INT, \
++ .mask_high = 1, \
++ .enb_mask = 0
++
++#define DED_EVENT(x) \
++ .base = MC_PCIE_CTRL_ADDR, \
++ .offset = DED_ERROR_INT, \
++ .mask_offset = DED_ERROR_INT_MASK, \
++ .mask_high = 1, \
++ .mask = DED_ERROR_INT_ ## x ## _INT, \
++ .enb_mask = 0
++
++#define LOCAL_EVENT(x) \
++ .base = MC_PCIE_BRIDGE_ADDR, \
++ .offset = ISTATUS_LOCAL, \
++ .mask_offset = IMASK_LOCAL, \
++ .mask_high = 0, \
++ .mask = x ## _MASK, \
++ .enb_mask = 0
++
++#define PCIE_EVENT_TO_EVENT_MAP(x) \
++ { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
++
++#define SEC_ERROR_TO_EVENT_MAP(x) \
++ { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
++
++#define DED_ERROR_TO_EVENT_MAP(x) \
++ { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
++
++#define LOCAL_STATUS_TO_EVENT_MAP(x) \
++ { x ## _MASK, EVENT_LOCAL_ ## x }
++
++struct event_map {
++ u32 reg_mask;
++ u32 event_bit;
++};
++
++struct mc_msi {
++ struct mutex lock; /* Protect used bitmap */
++ struct irq_domain *msi_domain;
++ struct irq_domain *dev_domain;
++ u32 num_vectors;
++ u64 vector_phy;
++ DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
++};
++
++struct mc_pcie {
++ void __iomem *axi_base_addr;
++ struct device *dev;
++ struct irq_domain *intx_domain;
++ struct irq_domain *event_domain;
++ raw_spinlock_t lock;
++ struct mc_msi msi;
++};
++
++struct cause {
++ const char *sym;
++ const char *str;
++};
++
++static const struct cause event_cause[NUM_EVENTS] = {
++ PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
++ PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
++ PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
++ SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"),
++ SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"),
++ SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"),
++ SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"),
++ DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"),
++ DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"),
++ DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"),
++ DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"),
++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
++ LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
++ LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
++ LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
++ LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
++ LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
++ LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
++ LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
++ LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
++ LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
++};
++
++static struct event_map pcie_event_to_event[] = {
++ PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
++ PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
++ PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT),
++};
++
++static struct event_map sec_error_to_event[] = {
++ SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
++ SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
++ SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
++ SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR),
++};
++
++static struct event_map ded_error_to_event[] = {
++ DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
++ DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
++ DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
++ DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR),
++};
++
++static struct event_map local_status_to_event[] = {
++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
++};
++
++static struct {
++ u32 base;
++ u32 offset;
++ u32 mask;
++ u32 shift;
++ u32 enb_mask;
++ u32 mask_high;
++ u32 mask_offset;
++} event_descs[] = {
++ { PCIE_EVENT(L2_EXIT) },
++ { PCIE_EVENT(HOTRST_EXIT) },
++ { PCIE_EVENT(DLUP_EXIT) },
++ { SEC_EVENT(TX_RAM_SEC_ERR) },
++ { SEC_EVENT(RX_RAM_SEC_ERR) },
++ { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
++ { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
++ { DED_EVENT(TX_RAM_DED_ERR) },
++ { DED_EVENT(RX_RAM_DED_ERR) },
++ { DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
++ { DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
++ { LOCAL_EVENT(DMA_END_ENGINE_0) },
++ { LOCAL_EVENT(DMA_END_ENGINE_1) },
++ { LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
++ { LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
++ { LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
++ { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
++ { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
++ { LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
++ { LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
++ { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
++ { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
++ { LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
++ { LOCAL_EVENT(PM_MSI_INT_INTX) },
++ { LOCAL_EVENT(PM_MSI_INT_MSI) },
++ { LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
++ { LOCAL_EVENT(PM_MSI_INT_EVENTS) },
++ { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) },
++};
++
++static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
++
++static struct mc_pcie *port;
++
++static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
++{
++ struct mc_msi *msi = &port->msi;
++ u16 reg;
++ u8 queue_size;
++
++ /* Fixup MSI enable flag */
++ reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++ reg |= PCI_MSI_FLAGS_ENABLE;
++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++
++ /* Fixup PCI MSI queue flags */
++ queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg);
++ reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size);
++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++
++ /* Fixup MSI addr fields */
++ writel_relaxed(lower_32_bits(msi->vector_phy),
++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO);
++ writel_relaxed(upper_32_bits(msi->vector_phy),
++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
++}
++
++static void mc_handle_msi(struct irq_desc *desc)
++{
++ struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct device *dev = port->dev;
++ struct mc_msi *msi = &port->msi;
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ unsigned long status;
++ u32 bit;
++ int ret;
++
++ chained_irq_enter(chip, desc);
++
++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++ if (status & PM_MSI_INT_MSI_MASK) {
++ writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
++ for_each_set_bit(bit, &status, msi->num_vectors) {
++ ret = generic_handle_domain_irq(msi->dev_domain, bit);
++ if (ret)
++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
++ bit);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void mc_msi_bottom_irq_ack(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ u32 bitpos = data->hwirq;
++
++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
++}
++
++static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ phys_addr_t addr = port->msi.vector_phy;
++
++ msg->address_lo = lower_32_bits(addr);
++ msg->address_hi = upper_32_bits(addr);
++ msg->data = data->hwirq;
++
++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
++ (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int mc_msi_set_affinity(struct irq_data *irq_data,
++ const struct cpumask *mask, bool force)
++{
++ return -EINVAL;
++}
++
++static struct irq_chip mc_msi_bottom_irq_chip = {
++ .name = "Microchip MSI",
++ .irq_ack = mc_msi_bottom_irq_ack,
++ .irq_compose_msi_msg = mc_compose_msi_msg,
++ .irq_set_affinity = mc_msi_set_affinity,
++};
++
++static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
++ unsigned int nr_irqs, void *args)
++{
++ struct mc_pcie *port = domain->host_data;
++ struct mc_msi *msi = &port->msi;
++ unsigned long bit;
++
++ mutex_lock(&msi->lock);
++ bit = find_first_zero_bit(msi->used, msi->num_vectors);
++ if (bit >= msi->num_vectors) {
++ mutex_unlock(&msi->lock);
++ return -ENOSPC;
++ }
++
++ set_bit(bit, msi->used);
++
++ irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
++ domain->host_data, handle_edge_irq, NULL, NULL);
++
++ mutex_unlock(&msi->lock);
++
++ return 0;
++}
++
++static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
++ unsigned int nr_irqs)
++{
++ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++ struct mc_pcie *port = irq_data_get_irq_chip_data(d);
++ struct mc_msi *msi = &port->msi;
++
++ mutex_lock(&msi->lock);
++
++ if (test_bit(d->hwirq, msi->used))
++ __clear_bit(d->hwirq, msi->used);
++ else
++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
++
++ mutex_unlock(&msi->lock);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++ .alloc = mc_irq_msi_domain_alloc,
++ .free = mc_irq_msi_domain_free,
++};
++
++static struct irq_chip mc_msi_irq_chip = {
++ .name = "Microchip PCIe MSI",
++ .irq_ack = irq_chip_ack_parent,
++ .irq_mask = pci_msi_mask_irq,
++ .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info mc_msi_domain_info = {
++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++ MSI_FLAG_PCI_MSIX),
++ .chip = &mc_msi_irq_chip,
++};
++
++static int mc_allocate_msi_domains(struct mc_pcie *port)
++{
++ struct device *dev = port->dev;
++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++ struct mc_msi *msi = &port->msi;
++
++ mutex_init(&port->msi.lock);
++
++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
++ &msi_domain_ops, port);
++ if (!msi->dev_domain) {
++ dev_err(dev, "failed to create IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
++ msi->dev_domain);
++ if (!msi->msi_domain) {
++ dev_err(dev, "failed to create MSI domain\n");
++ irq_domain_remove(msi->dev_domain);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static void mc_handle_intx(struct irq_desc *desc)
++{
++ struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct device *dev = port->dev;
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ unsigned long status;
++ u32 bit;
++ int ret;
++
++ chained_irq_enter(chip, desc);
++
++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++ if (status & PM_MSI_INT_INTX_MASK) {
++ status &= PM_MSI_INT_INTX_MASK;
++ status >>= PM_MSI_INT_INTX_SHIFT;
++ for_each_set_bit(bit, &status, PCI_NUM_INTX) {
++ ret = generic_handle_domain_irq(port->intx_domain, bit);
++ if (ret)
++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
++ bit);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void mc_ack_intx_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++
++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
++}
++
++static void mc_mask_intx_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ unsigned long flags;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++ u32 val;
++
++ raw_spin_lock_irqsave(&port->lock, flags);
++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++ val &= ~mask;
++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++ raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void mc_unmask_intx_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ unsigned long flags;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++ u32 val;
++
++ raw_spin_lock_irqsave(&port->lock, flags);
++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++ val |= mask;
++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++ raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static struct irq_chip mc_intx_irq_chip = {
++ .name = "Microchip PCIe INTx",
++ .irq_ack = mc_ack_intx_irq,
++ .irq_mask = mc_mask_intx_irq,
++ .irq_unmask = mc_unmask_intx_irq,
++};
++
++static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
++ irq_set_chip_data(irq, domain->host_data);
++
++ return 0;
++}
++
++static const struct irq_domain_ops intx_domain_ops = {
++ .map = mc_pcie_intx_map,
++};
++
++static inline u32 reg_to_event(u32 reg, struct event_map field)
++{
++ return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
++}
++
++static u32 pcie_events(struct mc_pcie *port)
++{
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++ u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT);
++ u32 val = 0;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
++ val |= reg_to_event(reg, pcie_event_to_event[i]);
++
++ return val;
++}
++
++static u32 sec_errors(struct mc_pcie *port)
++{
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++ u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT);
++ u32 val = 0;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
++ val |= reg_to_event(reg, sec_error_to_event[i]);
++
++ return val;
++}
++
++static u32 ded_errors(struct mc_pcie *port)
++{
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++ u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT);
++ u32 val = 0;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
++ val |= reg_to_event(reg, ded_error_to_event[i]);
++
++ return val;
++}
++
++static u32 local_events(struct mc_pcie *port)
++{
++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++ u32 val = 0;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
++ val |= reg_to_event(reg, local_status_to_event[i]);
++
++ return val;
++}
++
++static u32 get_events(struct mc_pcie *port)
++{
++ u32 events = 0;
++
++ events |= pcie_events(port);
++ events |= sec_errors(port);
++ events |= ded_errors(port);
++ events |= local_events(port);
++
++ return events;
++}
++
++static irqreturn_t mc_event_handler(int irq, void *dev_id)
++{
++ struct mc_pcie *port = dev_id;
++ struct device *dev = port->dev;
++ struct irq_data *data;
++
++ data = irq_domain_get_irq_data(port->event_domain, irq);
++
++ if (event_cause[data->hwirq].str)
++ dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
++ else
++ dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
++
++ return IRQ_HANDLED;
++}
++
++static void mc_handle_event(struct irq_desc *desc)
++{
++ struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ unsigned long events;
++ u32 bit;
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++
++ chained_irq_enter(chip, desc);
++
++ events = get_events(port);
++
++ for_each_set_bit(bit, &events, NUM_EVENTS)
++ generic_handle_domain_irq(port->event_domain, bit);
++
++ chained_irq_exit(chip, desc);
++}
++
++static void mc_ack_event_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ u32 event = data->hwirq;
++ void __iomem *addr;
++ u32 mask;
++
++ addr = port->axi_base_addr + event_descs[event].base +
++ event_descs[event].offset;
++ mask = event_descs[event].mask;
++ mask |= event_descs[event].enb_mask;
++
++ writel_relaxed(mask, addr);
++}
++
++static void mc_mask_event_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ u32 event = data->hwirq;
++ void __iomem *addr;
++ u32 mask;
++ u32 val;
++
++ addr = port->axi_base_addr + event_descs[event].base +
++ event_descs[event].mask_offset;
++ mask = event_descs[event].mask;
++ if (event_descs[event].enb_mask) {
++ mask <<= PCIE_EVENT_INT_ENB_SHIFT;
++ mask &= PCIE_EVENT_INT_ENB_MASK;
++ }
++
++ if (!event_descs[event].mask_high)
++ mask = ~mask;
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(addr);
++ if (event_descs[event].mask_high)
++ val |= mask;
++ else
++ val &= mask;
++
++ writel_relaxed(val, addr);
++ raw_spin_unlock(&port->lock);
++}
++
++static void mc_unmask_event_irq(struct irq_data *data)
++{
++ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ u32 event = data->hwirq;
++ void __iomem *addr;
++ u32 mask;
++ u32 val;
++
++ addr = port->axi_base_addr + event_descs[event].base +
++ event_descs[event].mask_offset;
++ mask = event_descs[event].mask;
++
++ if (event_descs[event].enb_mask)
++ mask <<= PCIE_EVENT_INT_ENB_SHIFT;
++
++ if (event_descs[event].mask_high)
++ mask = ~mask;
++
++ if (event_descs[event].enb_mask)
++ mask &= PCIE_EVENT_INT_ENB_MASK;
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(addr);
++ if (event_descs[event].mask_high)
++ val &= mask;
++ else
++ val |= mask;
++ writel_relaxed(val, addr);
++ raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip mc_event_irq_chip = {
++ .name = "Microchip PCIe EVENT",
++ .irq_ack = mc_ack_event_irq,
++ .irq_mask = mc_mask_event_irq,
++ .irq_unmask = mc_unmask_event_irq,
++};
++
++static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
++ irq_set_chip_data(irq, domain->host_data);
++
++ return 0;
++}
++
++static const struct irq_domain_ops event_domain_ops = {
++ .map = mc_pcie_event_map,
++};
++
++static inline void mc_pcie_deinit_clk(void *data)
++{
++ struct clk *clk = data;
++
++ clk_disable_unprepare(clk);
++}
++
++static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
++{
++ struct clk *clk;
++ int ret;
++
++ clk = devm_clk_get_optional(dev, id);
++ if (IS_ERR(clk))
++ return clk;
++ if (!clk)
++ return clk;
++
++ ret = clk_prepare_enable(clk);
++ if (ret)
++ return ERR_PTR(ret);
++
++ devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk);
++
++ return clk;
++}
++
++static int mc_pcie_init_clks(struct device *dev)
++{
++ int i;
++ struct clk *fic;
++
++ /*
++ * PCIe may be clocked via Fabric Interface using between 1 and 4
++ * clocks. Scan DT for clocks and enable them if present
++ */
++ for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
++ fic = mc_pcie_init_clk(dev, poss_clks[i]);
++ if (IS_ERR(fic))
++ return PTR_ERR(fic);
++ }
++
++ return 0;
++}
++
++static int mc_pcie_init_irq_domains(struct mc_pcie *port)
++{
++ struct device *dev = port->dev;
++ struct device_node *node = dev->of_node;
++ struct device_node *pcie_intc_node;
++
++ /* Setup INTx */
++ pcie_intc_node = of_get_next_child(node, NULL);
++ if (!pcie_intc_node) {
++ dev_err(dev, "failed to find PCIe Intc node\n");
++ return -EINVAL;
++ }
++
++ port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
++ &event_domain_ops, port);
++ if (!port->event_domain) {
++ dev_err(dev, "failed to get event domain\n");
++ of_node_put(pcie_intc_node);
++ return -ENOMEM;
++ }
++
++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
++
++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++ &intx_domain_ops, port);
++ if (!port->intx_domain) {
++ dev_err(dev, "failed to get an INTx IRQ domain\n");
++ of_node_put(pcie_intc_node);
++ return -ENOMEM;
++ }
++
++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
++
++ of_node_put(pcie_intc_node);
++ raw_spin_lock_init(&port->lock);
++
++ return mc_allocate_msi_domains(port);
++}
++
++static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++ phys_addr_t axi_addr, phys_addr_t pci_addr,
++ size_t size)
++{
++ u32 atr_sz = ilog2(size) - 1;
++ u32 val;
++
++ if (index == 0)
++ val = PCIE_CONFIG_INTERFACE;
++ else
++ val = PCIE_TX_RX_INTERFACE;
++
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_PARAM);
++
++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
++ ATR_IMPL_ENABLE;
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++ val = upper_32_bits(axi_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_SRC_ADDR);
++
++ val = lower_32_bits(pci_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++ val = upper_32_bits(pci_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
++
++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
++}
++
++static int mc_pcie_setup_windows(struct platform_device *pdev,
++ struct mc_pcie *port)
++{
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
++ struct resource_entry *entry;
++ u64 pci_addr;
++ u32 index = 1;
++
++ resource_list_for_each_entry(entry, &bridge->windows) {
++ if (resource_type(entry->res) == IORESOURCE_MEM) {
++ pci_addr = entry->res->start - entry->offset;
++ mc_pcie_setup_window(bridge_base_addr, index,
++ entry->res->start, pci_addr,
++ resource_size(entry->res));
++ index++;
++ }
++ }
++
++ return 0;
++}
++
++static inline void mc_clear_secs(struct mc_pcie *port)
++{
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++
++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
++ SEC_ERROR_INT);
++ writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT);
++}
++
++static inline void mc_clear_deds(struct mc_pcie *port)
++{
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++
++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
++ DED_ERROR_INT);
++ writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT);
++}
++
++static void mc_disable_interrupts(struct mc_pcie *port)
++{
++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++ u32 val;
++
++ /* Ensure ECC bypass is enabled */
++ val = ECC_CONTROL_TX_RAM_ECC_BYPASS |
++ ECC_CONTROL_RX_RAM_ECC_BYPASS |
++ ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS |
++ ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS;
++ writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
++
++ /* Disable SEC errors and clear any outstanding */
++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
++ SEC_ERROR_INT_MASK);
++ mc_clear_secs(port);
++
++ /* Disable DED errors and clear any outstanding */
++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
++ DED_ERROR_INT_MASK);
++ mc_clear_deds(port);
++
++ /* Disable local interrupts and clear any outstanding */
++ writel_relaxed(0, bridge_base_addr + IMASK_LOCAL);
++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL);
++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI);
++
++ /* Disable PCIe events and clear any outstanding */
++ val = PCIE_EVENT_INT_L2_EXIT_INT |
++ PCIE_EVENT_INT_HOTRST_EXIT_INT |
++ PCIE_EVENT_INT_DLUP_EXIT_INT |
++ PCIE_EVENT_INT_L2_EXIT_INT_MASK |
++ PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK |
++ PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
++ writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
++
++ /* Disable host interrupts and clear any outstanding */
++ writel_relaxed(0, bridge_base_addr + IMASK_HOST);
++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
++}
++
++static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
++{
++ struct device *dev = &pdev->dev;
++ int irq;
++ int i, intx_irq, msi_irq, event_irq;
++ int ret;
++
++ ret = mc_pcie_init_irq_domains(port);
++ if (ret) {
++ dev_err(dev, "failed creating IRQ domains\n");
++ return ret;
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return -ENODEV;
++
++ for (i = 0; i < NUM_EVENTS; i++) {
++ event_irq = irq_create_mapping(port->event_domain, i);
++ if (!event_irq) {
++ dev_err(dev, "failed to map hwirq %d\n", i);
++ return -ENXIO;
++ }
++
++ ret = devm_request_irq(dev, event_irq, mc_event_handler,
++ 0, event_cause[i].sym, port);
++ if (ret) {
++ dev_err(dev, "failed to request IRQ %d\n", event_irq);
++ return ret;
++ }
++ }
++
++ intx_irq = irq_create_mapping(port->event_domain,
++ EVENT_LOCAL_PM_MSI_INT_INTX);
++ if (!intx_irq) {
++ dev_err(dev, "failed to map INTx interrupt\n");
++ return -ENXIO;
++ }
++
++ /* Plug the INTx chained handler */
++ irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
++
++ msi_irq = irq_create_mapping(port->event_domain,
++ EVENT_LOCAL_PM_MSI_INT_MSI);
++ if (!msi_irq)
++ return -ENXIO;
++
++ /* Plug the MSI chained handler */
++ irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
++
++ /* Plug the main event chained handler */
++ irq_set_chained_handler_and_data(irq, mc_handle_event, port);
++
++ return 0;
++}
++
++static int mc_platform_init(struct pci_config_window *cfg)
++{
++ struct device *dev = cfg->parent;
++ struct platform_device *pdev = to_platform_device(dev);
++ void __iomem *bridge_base_addr =
++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ int ret;
++
++ /* Configure address translation table 0 for PCIe config space */
++ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
++ cfg->res.start,
++ resource_size(&cfg->res));
++
++ /* Need some fixups in config space */
++ mc_pcie_enable_msi(port, cfg->win);
++
++ /* Configure non-config space outbound ranges */
++ ret = mc_pcie_setup_windows(pdev, port);
++ if (ret)
++ return ret;
++
++ /* Address translation is up; safe to enable interrupts */
++ ret = mc_init_interrupts(pdev, port);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int mc_host_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ void __iomem *bridge_base_addr;
++ int ret;
++ u32 val;
++
++ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
++ if (!port)
++ return -ENOMEM;
++
++ port->dev = dev;
++
++ port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
++ if (IS_ERR(port->axi_base_addr))
++ return PTR_ERR(port->axi_base_addr);
++
++ mc_disable_interrupts(port);
++
++ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++
++ /* Allow enabling MSI by disabling MSI-X */
++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
++ val &= ~MSIX_CAP_MASK;
++ writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0);
++
++ /* Pick num vectors from bitfile programmed onto FPGA fabric */
++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
++ val &= NUM_MSI_MSGS_MASK;
++ val >>= NUM_MSI_MSGS_SHIFT;
++
++ port->msi.num_vectors = 1 << val;
++
++ /* Pick vector address from design */
++ port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
++
++ ret = mc_pcie_init_clks(dev);
++ if (ret) {
++ dev_err(dev, "failed to get clock resources, error %d\n", ret);
++ return -ENODEV;
++ }
++
++ return pci_host_common_probe(pdev);
++}
++
++static const struct pci_ecam_ops mc_ecam_ops = {
++ .init = mc_platform_init,
++ .pci_ops = {
++ .map_bus = pci_ecam_map_bus,
++ .read = pci_generic_config_read,
++ .write = pci_generic_config_write,
++ }
++};
++
++static const struct of_device_id mc_pcie_of_match[] = {
++ {
++ .compatible = "microchip,pcie-host-1.0",
++ .data = &mc_ecam_ops,
++ },
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, mc_pcie_of_match);
++
++static struct platform_driver mc_pcie_driver = {
++ .probe = mc_host_probe,
++ .driver = {
++ .name = "microchip-pcie",
++ .of_match_table = mc_pcie_of_match,
++ .suppress_bind_attrs = true,
++ },
++};
++
++builtin_platform_driver(mc_pcie_driver);
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Microchip PCIe host controller driver");
++MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
--- /dev/null
+From eca1b864bb2d4e8d9811506979560a89351c2e37 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:53 +0800
+Subject: [PATCH 016/116] PCI: microchip: Move PLDA IP register macros to
+ pcie-plda.h
+
+Move PLDA PCIe host controller IP registers macros to pcie-plda.h,
+including bridge registers and local IRQ event number.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 108 +++---------------
+ drivers/pci/controller/plda/pcie-plda.h | 108 ++++++++++++++++++
+ 2 files changed, 124 insertions(+), 92 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-plda.h
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -19,6 +19,7 @@
+ #include <linux/platform_device.h>
+
+ #include "../../pci.h"
++#include "pcie-plda.h"
+
+ /* Number of MSI IRQs */
+ #define MC_MAX_NUM_MSI_IRQS 32
+@@ -30,84 +31,6 @@
+ #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR)
+ #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR)
+
+-/* PCIe Bridge Phy Regs */
+-#define PCIE_PCI_IRQ_DW0 0xa8
+-#define MSIX_CAP_MASK BIT(31)
+-#define NUM_MSI_MSGS_MASK GENMASK(6, 4)
+-#define NUM_MSI_MSGS_SHIFT 4
+-
+-#define IMASK_LOCAL 0x180
+-#define DMA_END_ENGINE_0_MASK 0x00000000u
+-#define DMA_END_ENGINE_0_SHIFT 0
+-#define DMA_END_ENGINE_1_MASK 0x00000000u
+-#define DMA_END_ENGINE_1_SHIFT 1
+-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u
+-#define DMA_ERROR_ENGINE_0_SHIFT 8
+-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u
+-#define DMA_ERROR_ENGINE_1_SHIFT 9
+-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u
+-#define A_ATR_EVT_POST_ERR_SHIFT 16
+-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u
+-#define A_ATR_EVT_FETCH_ERR_SHIFT 17
+-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u
+-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18
+-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u
+-#define A_ATR_EVT_DOORBELL_SHIFT 19
+-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u
+-#define P_ATR_EVT_POST_ERR_SHIFT 20
+-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u
+-#define P_ATR_EVT_FETCH_ERR_SHIFT 21
+-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u
+-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22
+-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u
+-#define P_ATR_EVT_DOORBELL_SHIFT 23
+-#define PM_MSI_INT_INTA_MASK 0x01000000u
+-#define PM_MSI_INT_INTA_SHIFT 24
+-#define PM_MSI_INT_INTB_MASK 0x02000000u
+-#define PM_MSI_INT_INTB_SHIFT 25
+-#define PM_MSI_INT_INTC_MASK 0x04000000u
+-#define PM_MSI_INT_INTC_SHIFT 26
+-#define PM_MSI_INT_INTD_MASK 0x08000000u
+-#define PM_MSI_INT_INTD_SHIFT 27
+-#define PM_MSI_INT_INTX_MASK 0x0f000000u
+-#define PM_MSI_INT_INTX_SHIFT 24
+-#define PM_MSI_INT_MSI_MASK 0x10000000u
+-#define PM_MSI_INT_MSI_SHIFT 28
+-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u
+-#define PM_MSI_INT_AER_EVT_SHIFT 29
+-#define PM_MSI_INT_EVENTS_MASK 0x40000000u
+-#define PM_MSI_INT_EVENTS_SHIFT 30
+-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u
+-#define PM_MSI_INT_SYS_ERR_SHIFT 31
+-#define NUM_LOCAL_EVENTS 15
+-#define ISTATUS_LOCAL 0x184
+-#define IMASK_HOST 0x188
+-#define ISTATUS_HOST 0x18c
+-#define IMSI_ADDR 0x190
+-#define ISTATUS_MSI 0x194
+-
+-/* PCIe Master table init defines */
+-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u
+-#define ATR0_PCIE_ATR_SIZE 0x25
+-#define ATR0_PCIE_ATR_SIZE_SHIFT 1
+-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu
+-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u
+-
+-/* PCIe AXI slave table init defines */
+-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u
+-#define ATR_SIZE_SHIFT 1
+-#define ATR_IMPL_ENABLE 1
+-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu
+-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u
+-#define PCIE_TX_RX_INTERFACE 0x00000000u
+-#define PCIE_CONFIG_INTERFACE 0x00000001u
+-
+-#define ATR_ENTRY_SIZE 32
+-
+ /* PCIe Controller Phy Regs */
+ #define SEC_ERROR_EVENT_CNT 0x20
+ #define DED_ERROR_EVENT_CNT 0x24
+@@ -179,20 +102,21 @@
+ #define EVENT_LOCAL_DMA_END_ENGINE_1 12
+ #define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13
+ #define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14
+-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15
+-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16
+-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17
+-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18
+-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19
+-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20
+-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21
+-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22
+-#define EVENT_LOCAL_PM_MSI_INT_INTX 23
+-#define EVENT_LOCAL_PM_MSI_INT_MSI 24
+-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25
+-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26
+-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27
+-#define NUM_EVENTS 28
++#define NUM_MC_EVENTS 15
++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_AXI_POST_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_AXI_FETCH_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_AXI_DISCARD_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_AXI_DOORBELL)
++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_PCIE_POST_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_PCIE_FETCH_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_PCIE_DISCARD_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_PCIE_DOORBELL)
++#define EVENT_LOCAL_PM_MSI_INT_INTX (NUM_MC_EVENTS + PLDA_INTX)
++#define EVENT_LOCAL_PM_MSI_INT_MSI (NUM_MC_EVENTS + PLDA_MSI)
++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT (NUM_MC_EVENTS + PLDA_AER_EVENT)
++#define EVENT_LOCAL_PM_MSI_INT_EVENTS (NUM_MC_EVENTS + PLDA_MISC_EVENTS)
++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR (NUM_MC_EVENTS + PLDA_SYS_ERR)
++#define NUM_EVENTS (NUM_MC_EVENTS + PLDA_INT_EVENT_NUM)
+
+ #define PCIE_EVENT_CAUSE(x, s) \
+ [EVENT_PCIE_ ## x] = { __stringify(x), s }
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -0,0 +1,108 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PLDA PCIe host controller driver
++ */
++
++#ifndef _PCIE_PLDA_H
++#define _PCIE_PLDA_H
++
++/* PCIe Bridge Phy Regs */
++#define PCIE_PCI_IRQ_DW0 0xa8
++#define MSIX_CAP_MASK BIT(31)
++#define NUM_MSI_MSGS_MASK GENMASK(6, 4)
++#define NUM_MSI_MSGS_SHIFT 4
++
++#define IMASK_LOCAL 0x180
++#define DMA_END_ENGINE_0_MASK 0x00000000u
++#define DMA_END_ENGINE_0_SHIFT 0
++#define DMA_END_ENGINE_1_MASK 0x00000000u
++#define DMA_END_ENGINE_1_SHIFT 1
++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u
++#define DMA_ERROR_ENGINE_0_SHIFT 8
++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u
++#define DMA_ERROR_ENGINE_1_SHIFT 9
++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u
++#define A_ATR_EVT_POST_ERR_SHIFT 16
++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u
++#define A_ATR_EVT_FETCH_ERR_SHIFT 17
++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u
++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18
++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u
++#define A_ATR_EVT_DOORBELL_SHIFT 19
++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u
++#define P_ATR_EVT_POST_ERR_SHIFT 20
++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u
++#define P_ATR_EVT_FETCH_ERR_SHIFT 21
++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u
++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22
++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u
++#define P_ATR_EVT_DOORBELL_SHIFT 23
++#define PM_MSI_INT_INTA_MASK 0x01000000u
++#define PM_MSI_INT_INTA_SHIFT 24
++#define PM_MSI_INT_INTB_MASK 0x02000000u
++#define PM_MSI_INT_INTB_SHIFT 25
++#define PM_MSI_INT_INTC_MASK 0x04000000u
++#define PM_MSI_INT_INTC_SHIFT 26
++#define PM_MSI_INT_INTD_MASK 0x08000000u
++#define PM_MSI_INT_INTD_SHIFT 27
++#define PM_MSI_INT_INTX_MASK 0x0f000000u
++#define PM_MSI_INT_INTX_SHIFT 24
++#define PM_MSI_INT_MSI_MASK 0x10000000u
++#define PM_MSI_INT_MSI_SHIFT 28
++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u
++#define PM_MSI_INT_AER_EVT_SHIFT 29
++#define PM_MSI_INT_EVENTS_MASK 0x40000000u
++#define PM_MSI_INT_EVENTS_SHIFT 30
++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u
++#define PM_MSI_INT_SYS_ERR_SHIFT 31
++#define NUM_LOCAL_EVENTS 15
++#define ISTATUS_LOCAL 0x184
++#define IMASK_HOST 0x188
++#define ISTATUS_HOST 0x18c
++#define IMSI_ADDR 0x190
++#define ISTATUS_MSI 0x194
++
++/* PCIe Master table init defines */
++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u
++#define ATR0_PCIE_ATR_SIZE 0x25
++#define ATR0_PCIE_ATR_SIZE_SHIFT 1
++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu
++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u
++
++/* PCIe AXI slave table init defines */
++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u
++#define ATR_SIZE_SHIFT 1
++#define ATR_IMPL_ENABLE 1
++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu
++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u
++#define PCIE_TX_RX_INTERFACE 0x00000000u
++#define PCIE_CONFIG_INTERFACE 0x00000001u
++
++#define ATR_ENTRY_SIZE 32
++
++enum plda_int_event {
++ PLDA_AXI_POST_ERR,
++ PLDA_AXI_FETCH_ERR,
++ PLDA_AXI_DISCARD_ERR,
++ PLDA_AXI_DOORBELL,
++ PLDA_PCIE_POST_ERR,
++ PLDA_PCIE_FETCH_ERR,
++ PLDA_PCIE_DISCARD_ERR,
++ PLDA_PCIE_DOORBELL,
++ PLDA_INTX,
++ PLDA_MSI,
++ PLDA_AER_EVENT,
++ PLDA_MISC_EVENTS,
++ PLDA_SYS_ERR,
++ PLDA_INT_EVENT_NUM
++};
++
++#define PLDA_NUM_DMA_EVENTS 16
++
++#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++
++#endif
--- /dev/null
+From 6ee4d4568314425079ae88229bb9abbff9b92b8b Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:54 +0800
+Subject: [PATCH 017/116] PCI: microchip: Add bridge_addr field to struct
+ mc_pcie
+
+For bridge address base is common PLDA field, Add this to struct mc_pcie
+first.
+
+INTx and MSI codes interrupts codes will get the bridge base address from
+port->bridge_addr. These codes will be changed to common codes.
+axi_base_addr is Microchip its own data.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 23 ++++++++-----------
+ 1 file changed, 9 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -195,6 +195,7 @@ struct mc_pcie {
+ struct irq_domain *event_domain;
+ raw_spinlock_t lock;
+ struct mc_msi msi;
++ void __iomem *bridge_addr;
+ };
+
+ struct cause {
+@@ -339,8 +340,7 @@ static void mc_handle_msi(struct irq_des
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct device *dev = port->dev;
+ struct mc_msi *msi = &port->msi;
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long status;
+ u32 bit;
+ int ret;
+@@ -365,8 +365,7 @@ static void mc_handle_msi(struct irq_des
+ static void mc_msi_bottom_irq_ack(struct irq_data *data)
+ {
+ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ u32 bitpos = data->hwirq;
+
+ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+@@ -488,8 +487,7 @@ static void mc_handle_intx(struct irq_de
+ struct mc_pcie *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct device *dev = port->dev;
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long status;
+ u32 bit;
+ int ret;
+@@ -514,8 +512,7 @@ static void mc_handle_intx(struct irq_de
+ static void mc_ack_intx_irq(struct irq_data *data)
+ {
+ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+
+ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+@@ -524,8 +521,7 @@ static void mc_ack_intx_irq(struct irq_d
+ static void mc_mask_intx_irq(struct irq_data *data)
+ {
+ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long flags;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+ u32 val;
+@@ -540,8 +536,7 @@ static void mc_mask_intx_irq(struct irq_
+ static void mc_unmask_intx_irq(struct irq_data *data)
+ {
+ struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long flags;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+ u32 val;
+@@ -896,8 +891,7 @@ static void mc_pcie_setup_window(void __
+ static int mc_pcie_setup_windows(struct platform_device *pdev,
+ struct mc_pcie *port)
+ {
+- void __iomem *bridge_base_addr =
+- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ void __iomem *bridge_base_addr = port->bridge_addr;
+ struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+ struct resource_entry *entry;
+ u64 pci_addr;
+@@ -1081,6 +1075,7 @@ static int mc_host_probe(struct platform
+ mc_disable_interrupts(port);
+
+ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++ port->bridge_addr = bridge_base_addr;
+
+ /* Allow enabling MSI by disabling MSI-X */
+ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
--- /dev/null
+From 7c1c679bdd0b6b727248edbee77836024c935f91 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:55 +0800
+Subject: [PATCH 018/116] PCI: microchip: Rename two PCIe data structures
+
+Add PLDA PCIe related data structures by rename data structure name from
+mc_* to plda_*.
+
+axi_base_addr is stayed in struct mc_pcie for it's microchip its own data.
+
+The event interrupt codes is still using struct mc_pcie because the event
+interrupt codes can not be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 96 ++++++++++---------
+ 1 file changed, 53 insertions(+), 43 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -22,7 +22,7 @@
+ #include "pcie-plda.h"
+
+ /* Number of MSI IRQs */
+-#define MC_MAX_NUM_MSI_IRQS 32
++#define PLDA_MAX_NUM_MSI_IRQS 32
+
+ /* PCIe Bridge Phy and Controller Phy offsets */
+ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u
+@@ -179,25 +179,29 @@ struct event_map {
+ u32 event_bit;
+ };
+
+-struct mc_msi {
++struct plda_msi {
+ struct mutex lock; /* Protect used bitmap */
+ struct irq_domain *msi_domain;
+ struct irq_domain *dev_domain;
+ u32 num_vectors;
+ u64 vector_phy;
+- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
+ };
+
+-struct mc_pcie {
+- void __iomem *axi_base_addr;
++struct plda_pcie_rp {
+ struct device *dev;
+ struct irq_domain *intx_domain;
+ struct irq_domain *event_domain;
+ raw_spinlock_t lock;
+- struct mc_msi msi;
++ struct plda_msi msi;
+ void __iomem *bridge_addr;
+ };
+
++struct mc_pcie {
++ struct plda_pcie_rp plda;
++ void __iomem *axi_base_addr;
++};
++
+ struct cause {
+ const char *sym;
+ const char *str;
+@@ -313,7 +317,7 @@ static struct mc_pcie *port;
+
+ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
+ {
+- struct mc_msi *msi = &port->msi;
++ struct plda_msi *msi = &port->plda.msi;
+ u16 reg;
+ u8 queue_size;
+
+@@ -336,10 +340,10 @@ static void mc_pcie_enable_msi(struct mc
+
+ static void mc_handle_msi(struct irq_desc *desc)
+ {
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct device *dev = port->dev;
+- struct mc_msi *msi = &port->msi;
++ struct plda_msi *msi = &port->msi;
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long status;
+ u32 bit;
+@@ -364,7 +368,7 @@ static void mc_handle_msi(struct irq_des
+
+ static void mc_msi_bottom_irq_ack(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ u32 bitpos = data->hwirq;
+
+@@ -373,7 +377,7 @@ static void mc_msi_bottom_irq_ack(struct
+
+ static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ phys_addr_t addr = port->msi.vector_phy;
+
+ msg->address_lo = lower_32_bits(addr);
+@@ -400,8 +404,8 @@ static struct irq_chip mc_msi_bottom_irq
+ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *args)
+ {
+- struct mc_pcie *port = domain->host_data;
+- struct mc_msi *msi = &port->msi;
++ struct plda_pcie_rp *port = domain->host_data;
++ struct plda_msi *msi = &port->msi;
+ unsigned long bit;
+
+ mutex_lock(&msi->lock);
+@@ -425,8 +429,8 @@ static void mc_irq_msi_domain_free(struc
+ unsigned int nr_irqs)
+ {
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+- struct mc_pcie *port = irq_data_get_irq_chip_data(d);
+- struct mc_msi *msi = &port->msi;
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
++ struct plda_msi *msi = &port->msi;
+
+ mutex_lock(&msi->lock);
+
+@@ -456,11 +460,11 @@ static struct msi_domain_info mc_msi_dom
+ .chip = &mc_msi_irq_chip,
+ };
+
+-static int mc_allocate_msi_domains(struct mc_pcie *port)
++static int mc_allocate_msi_domains(struct plda_pcie_rp *port)
+ {
+ struct device *dev = port->dev;
+ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+- struct mc_msi *msi = &port->msi;
++ struct plda_msi *msi = &port->msi;
+
+ mutex_init(&port->msi.lock);
+
+@@ -484,7 +488,7 @@ static int mc_allocate_msi_domains(struc
+
+ static void mc_handle_intx(struct irq_desc *desc)
+ {
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct device *dev = port->dev;
+ void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -511,7 +515,7 @@ static void mc_handle_intx(struct irq_de
+
+ static void mc_ack_intx_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+
+@@ -520,7 +524,7 @@ static void mc_ack_intx_irq(struct irq_d
+
+ static void mc_mask_intx_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long flags;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+@@ -535,7 +539,7 @@ static void mc_mask_intx_irq(struct irq_
+
+ static void mc_unmask_intx_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ unsigned long flags;
+ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+@@ -625,21 +629,22 @@ static u32 local_events(struct mc_pcie *
+ return val;
+ }
+
+-static u32 get_events(struct mc_pcie *port)
++static u32 get_events(struct plda_pcie_rp *port)
+ {
++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+ u32 events = 0;
+
+- events |= pcie_events(port);
+- events |= sec_errors(port);
+- events |= ded_errors(port);
+- events |= local_events(port);
++ events |= pcie_events(mc_port);
++ events |= sec_errors(mc_port);
++ events |= ded_errors(mc_port);
++ events |= local_events(mc_port);
+
+ return events;
+ }
+
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+- struct mc_pcie *port = dev_id;
++ struct plda_pcie_rp *port = dev_id;
+ struct device *dev = port->dev;
+ struct irq_data *data;
+
+@@ -655,7 +660,7 @@ static irqreturn_t mc_event_handler(int
+
+ static void mc_handle_event(struct irq_desc *desc)
+ {
+- struct mc_pcie *port = irq_desc_get_handler_data(desc);
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ unsigned long events;
+ u32 bit;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -672,12 +677,13 @@ static void mc_handle_event(struct irq_d
+
+ static void mc_ack_event_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+ u32 event = data->hwirq;
+ void __iomem *addr;
+ u32 mask;
+
+- addr = port->axi_base_addr + event_descs[event].base +
++ addr = mc_port->axi_base_addr + event_descs[event].base +
+ event_descs[event].offset;
+ mask = event_descs[event].mask;
+ mask |= event_descs[event].enb_mask;
+@@ -687,13 +693,14 @@ static void mc_ack_event_irq(struct irq_
+
+ static void mc_mask_event_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+ u32 event = data->hwirq;
+ void __iomem *addr;
+ u32 mask;
+ u32 val;
+
+- addr = port->axi_base_addr + event_descs[event].base +
++ addr = mc_port->axi_base_addr + event_descs[event].base +
+ event_descs[event].mask_offset;
+ mask = event_descs[event].mask;
+ if (event_descs[event].enb_mask) {
+@@ -717,13 +724,14 @@ static void mc_mask_event_irq(struct irq
+
+ static void mc_unmask_event_irq(struct irq_data *data)
+ {
+- struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+ u32 event = data->hwirq;
+ void __iomem *addr;
+ u32 mask;
+ u32 val;
+
+- addr = port->axi_base_addr + event_descs[event].base +
++ addr = mc_port->axi_base_addr + event_descs[event].base +
+ event_descs[event].mask_offset;
+ mask = event_descs[event].mask;
+
+@@ -811,7 +819,7 @@ static int mc_pcie_init_clks(struct devi
+ return 0;
+ }
+
+-static int mc_pcie_init_irq_domains(struct mc_pcie *port)
++static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+ struct device *dev = port->dev;
+ struct device_node *node = dev->of_node;
+@@ -889,7 +897,7 @@ static void mc_pcie_setup_window(void __
+ }
+
+ static int mc_pcie_setup_windows(struct platform_device *pdev,
+- struct mc_pcie *port)
++ struct plda_pcie_rp *port)
+ {
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+@@ -970,7 +978,7 @@ static void mc_disable_interrupts(struct
+ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+
+-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
++static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
+ {
+ struct device *dev = &pdev->dev;
+ int irq;
+@@ -1043,12 +1051,12 @@ static int mc_platform_init(struct pci_c
+ mc_pcie_enable_msi(port, cfg->win);
+
+ /* Configure non-config space outbound ranges */
+- ret = mc_pcie_setup_windows(pdev, port);
++ ret = mc_pcie_setup_windows(pdev, &port->plda);
+ if (ret)
+ return ret;
+
+ /* Address translation is up; safe to enable interrupts */
+- ret = mc_init_interrupts(pdev, port);
++ ret = mc_init_interrupts(pdev, &port->plda);
+ if (ret)
+ return ret;
+
+@@ -1059,6 +1067,7 @@ static int mc_host_probe(struct platform
+ {
+ struct device *dev = &pdev->dev;
+ void __iomem *bridge_base_addr;
++ struct plda_pcie_rp *plda;
+ int ret;
+ u32 val;
+
+@@ -1066,7 +1075,8 @@ static int mc_host_probe(struct platform
+ if (!port)
+ return -ENOMEM;
+
+- port->dev = dev;
++ plda = &port->plda;
++ plda->dev = dev;
+
+ port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(port->axi_base_addr))
+@@ -1075,7 +1085,7 @@ static int mc_host_probe(struct platform
+ mc_disable_interrupts(port);
+
+ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+- port->bridge_addr = bridge_base_addr;
++ plda->bridge_addr = bridge_base_addr;
+
+ /* Allow enabling MSI by disabling MSI-X */
+ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+@@ -1087,10 +1097,10 @@ static int mc_host_probe(struct platform
+ val &= NUM_MSI_MSGS_MASK;
+ val >>= NUM_MSI_MSGS_SHIFT;
+
+- port->msi.num_vectors = 1 << val;
++ plda->msi.num_vectors = 1 << val;
+
+ /* Pick vector address from design */
+- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
++ plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
+
+ ret = mc_pcie_init_clks(dev);
+ if (ret) {
--- /dev/null
+From a53cf9b237dd53c9538b51a8df592888935c8411 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:56 +0800
+Subject: [PATCH 019/116] PCI: microchip: Move PCIe host data structures to
+ plda-pcie.h
+
+Move the common data structures definition to head file for these two data
+structures can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 20 ------------------
+ drivers/pci/controller/plda/pcie-plda.h | 21 +++++++++++++++++++
+ 2 files changed, 21 insertions(+), 20 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -21,9 +21,6 @@
+ #include "../../pci.h"
+ #include "pcie-plda.h"
+
+-/* Number of MSI IRQs */
+-#define PLDA_MAX_NUM_MSI_IRQS 32
+-
+ /* PCIe Bridge Phy and Controller Phy offsets */
+ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u
+ #define MC_PCIE1_CTRL_ADDR 0x0000a000u
+@@ -179,23 +176,6 @@ struct event_map {
+ u32 event_bit;
+ };
+
+-struct plda_msi {
+- struct mutex lock; /* Protect used bitmap */
+- struct irq_domain *msi_domain;
+- struct irq_domain *dev_domain;
+- u32 num_vectors;
+- u64 vector_phy;
+- DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
+-};
+-
+-struct plda_pcie_rp {
+- struct device *dev;
+- struct irq_domain *intx_domain;
+- struct irq_domain *event_domain;
+- raw_spinlock_t lock;
+- struct plda_msi msi;
+- void __iomem *bridge_addr;
+-};
+
+ struct mc_pcie {
+ struct plda_pcie_rp plda;
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -6,6 +6,9 @@
+ #ifndef _PCIE_PLDA_H
+ #define _PCIE_PLDA_H
+
++/* Number of MSI IRQs */
++#define PLDA_MAX_NUM_MSI_IRQS 32
++
+ /* PCIe Bridge Phy Regs */
+ #define PCIE_PCI_IRQ_DW0 0xa8
+ #define MSIX_CAP_MASK BIT(31)
+@@ -105,4 +108,22 @@ enum plda_int_event {
+
+ #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
+
++struct plda_msi {
++ struct mutex lock; /* Protect used bitmap */
++ struct irq_domain *msi_domain;
++ struct irq_domain *dev_domain;
++ u32 num_vectors;
++ u64 vector_phy;
++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
++};
++
++struct plda_pcie_rp {
++ struct device *dev;
++ struct irq_domain *intx_domain;
++ struct irq_domain *event_domain;
++ raw_spinlock_t lock;
++ struct plda_msi msi;
++ void __iomem *bridge_addr;
++};
++
+ #endif
--- /dev/null
+From d0e56d1ef7398bbf76be6e48d77943cbf644688e Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:57 +0800
+Subject: [PATCH 020/116] PCI: microchip: Rename two setup functions
+
+Rename two setup functions to plda prefix. Prepare to re-use these two
+setup function.
+
+For two setup functions names are similar, rename mc_pcie_setup_windows()
+to plda_pcie_setup_iomems().
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 24 +++++++++----------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -838,9 +838,9 @@ static int mc_pcie_init_irq_domains(stru
+ return mc_allocate_msi_domains(port);
+ }
+
+-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+- phys_addr_t axi_addr, phys_addr_t pci_addr,
+- size_t size)
++static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++ phys_addr_t axi_addr, phys_addr_t pci_addr,
++ size_t size)
+ {
+ u32 atr_sz = ilog2(size) - 1;
+ u32 val;
+@@ -876,8 +876,8 @@ static void mc_pcie_setup_window(void __
+ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+ }
+
+-static int mc_pcie_setup_windows(struct platform_device *pdev,
+- struct plda_pcie_rp *port)
++static int plda_pcie_setup_iomems(struct platform_device *pdev,
++ struct plda_pcie_rp *port)
+ {
+ void __iomem *bridge_base_addr = port->bridge_addr;
+ struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+@@ -888,9 +888,9 @@ static int mc_pcie_setup_windows(struct
+ resource_list_for_each_entry(entry, &bridge->windows) {
+ if (resource_type(entry->res) == IORESOURCE_MEM) {
+ pci_addr = entry->res->start - entry->offset;
+- mc_pcie_setup_window(bridge_base_addr, index,
+- entry->res->start, pci_addr,
+- resource_size(entry->res));
++ plda_pcie_setup_window(bridge_base_addr, index,
++ entry->res->start, pci_addr,
++ resource_size(entry->res));
+ index++;
+ }
+ }
+@@ -1023,15 +1023,15 @@ static int mc_platform_init(struct pci_c
+ int ret;
+
+ /* Configure address translation table 0 for PCIe config space */
+- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
+- cfg->res.start,
+- resource_size(&cfg->res));
++ plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
++ cfg->res.start,
++ resource_size(&cfg->res));
+
+ /* Need some fixups in config space */
+ mc_pcie_enable_msi(port, cfg->win);
+
+ /* Configure non-config space outbound ranges */
+- ret = mc_pcie_setup_windows(pdev, &port->plda);
++ ret = plda_pcie_setup_iomems(pdev, &port->plda);
+ if (ret)
+ return ret;
+
--- /dev/null
+From 2fd7c88ef318fd39023ce1eb73f37a29fbd25d74 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:58 +0800
+Subject: [PATCH 021/116] PCI: microchip: Change the argument of
+ plda_pcie_setup_iomems()
+
+If other vendor do not select PCI_HOST_COMMON, the driver data is not
+struct pci_host_bridge.
+
+Move calling platform_get_drvdata() to mc_platform_init().
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -876,11 +876,10 @@ static void plda_pcie_setup_window(void
+ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+ }
+
+-static int plda_pcie_setup_iomems(struct platform_device *pdev,
++static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+ struct plda_pcie_rp *port)
+ {
+ void __iomem *bridge_base_addr = port->bridge_addr;
+- struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+ struct resource_entry *entry;
+ u64 pci_addr;
+ u32 index = 1;
+@@ -1018,6 +1017,7 @@ static int mc_platform_init(struct pci_c
+ {
+ struct device *dev = cfg->parent;
+ struct platform_device *pdev = to_platform_device(dev);
++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+ void __iomem *bridge_base_addr =
+ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+ int ret;
+@@ -1031,7 +1031,7 @@ static int mc_platform_init(struct pci_c
+ mc_pcie_enable_msi(port, cfg->win);
+
+ /* Configure non-config space outbound ranges */
+- ret = plda_pcie_setup_iomems(pdev, &port->plda);
++ ret = plda_pcie_setup_iomems(bridge, &port->plda);
+ if (ret)
+ return ret;
+
--- /dev/null
+From 201ce99897ff5fff2612cb633406e90c1b2acbcf Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:59 +0800
+Subject: [PATCH 022/116] PCI: microchip: Move setup functions to
+ pcie-plda-host.c
+
+Move setup functions to common pcie-plda-host.c. So these two functions
+can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/Kconfig | 4 +
+ drivers/pci/controller/plda/Makefile | 1 +
+ .../pci/controller/plda/pcie-microchip-host.c | 59 ---------------
+ drivers/pci/controller/plda/pcie-plda-host.c | 74 +++++++++++++++++++
+ drivers/pci/controller/plda/pcie-plda.h | 5 ++
+ 5 files changed, 84 insertions(+), 59 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-plda-host.c
+
+--- a/drivers/pci/controller/plda/Kconfig
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -3,10 +3,14 @@
+ menu "PLDA-based PCIe controllers"
+ depends on PCI
+
++config PCIE_PLDA_HOST
++ bool
++
+ config PCIE_MICROCHIP_HOST
+ tristate "Microchip AXI PCIe controller"
+ depends on PCI_MSI && OF
+ select PCI_HOST_COMMON
++ select PCIE_PLDA_HOST
+ help
+ Say Y here if you want kernel to support the Microchip AXI PCIe
+ Host Bridge driver.
+--- a/drivers/pci/controller/plda/Makefile
++++ b/drivers/pci/controller/plda/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o
+ obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -838,65 +838,6 @@ static int mc_pcie_init_irq_domains(stru
+ return mc_allocate_msi_domains(port);
+ }
+
+-static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+- phys_addr_t axi_addr, phys_addr_t pci_addr,
+- size_t size)
+-{
+- u32 atr_sz = ilog2(size) - 1;
+- u32 val;
+-
+- if (index == 0)
+- val = PCIE_CONFIG_INTERFACE;
+- else
+- val = PCIE_TX_RX_INTERFACE;
+-
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_PARAM);
+-
+- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
+- ATR_IMPL_ENABLE;
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_SRCADDR_PARAM);
+-
+- val = upper_32_bits(axi_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_SRC_ADDR);
+-
+- val = lower_32_bits(pci_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
+-
+- val = upper_32_bits(pci_addr);
+- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+- ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
+-
+- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+-}
+-
+-static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+- struct plda_pcie_rp *port)
+-{
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- struct resource_entry *entry;
+- u64 pci_addr;
+- u32 index = 1;
+-
+- resource_list_for_each_entry(entry, &bridge->windows) {
+- if (resource_type(entry->res) == IORESOURCE_MEM) {
+- pci_addr = entry->res->start - entry->offset;
+- plda_pcie_setup_window(bridge_base_addr, index,
+- entry->res->start, pci_addr,
+- resource_size(entry->res));
+- index++;
+- }
+- }
+-
+- return 0;
+-}
+-
+ static inline void mc_clear_secs(struct mc_pcie *port)
+ {
+ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -0,0 +1,74 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PLDA PCIe XpressRich host controller driver
++ *
++ * Copyright (C) 2023 Microchip Co. Ltd
++ *
++ * Author: Daire McNamara <daire.mcnamara@microchip.com>
++ */
++
++#include <linux/pci_regs.h>
++#include <linux/pci-ecam.h>
++
++#include "pcie-plda.h"
++
++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++ phys_addr_t axi_addr, phys_addr_t pci_addr,
++ size_t size)
++{
++ u32 atr_sz = ilog2(size) - 1;
++ u32 val;
++
++ if (index == 0)
++ val = PCIE_CONFIG_INTERFACE;
++ else
++ val = PCIE_TX_RX_INTERFACE;
++
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_PARAM);
++
++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
++ ATR_IMPL_ENABLE;
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++ val = upper_32_bits(axi_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_SRC_ADDR);
++
++ val = lower_32_bits(pci_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++ val = upper_32_bits(pci_addr);
++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
++
++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_setup_window);
++
++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
++ struct plda_pcie_rp *port)
++{
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ struct resource_entry *entry;
++ u64 pci_addr;
++ u32 index = 1;
++
++ resource_list_for_each_entry(entry, &bridge->windows) {
++ if (resource_type(entry->res) == IORESOURCE_MEM) {
++ pci_addr = entry->res->start - entry->offset;
++ plda_pcie_setup_window(bridge_base_addr, index,
++ entry->res->start, pci_addr,
++ resource_size(entry->res));
++ index++;
++ }
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -126,4 +126,9 @@ struct plda_pcie_rp {
+ void __iomem *bridge_addr;
+ };
+
++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++ phys_addr_t axi_addr, phys_addr_t pci_addr,
++ size_t size);
++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
++ struct plda_pcie_rp *port);
+ #endif
--- /dev/null
+From 2a48bc45dcf8cbe736b594d013cfa9d682214c43 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:00 +0800
+Subject: [PATCH 023/116] PCI: microchip: Rename interrupt related functions
+
+Rename mc_* to plda_* for IRQ functions and related IRQ domain ops data
+instances.
+
+MSI, INTx interrupt code and IRQ init code are all can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 109 +++++++++---------
+ 1 file changed, 57 insertions(+), 52 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -318,7 +318,7 @@ static void mc_pcie_enable_msi(struct mc
+ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+ }
+
+-static void mc_handle_msi(struct irq_desc *desc)
++static void plda_handle_msi(struct irq_desc *desc)
+ {
+ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -346,7 +346,7 @@ static void mc_handle_msi(struct irq_des
+ chained_irq_exit(chip, desc);
+ }
+
+-static void mc_msi_bottom_irq_ack(struct irq_data *data)
++static void plda_msi_bottom_irq_ack(struct irq_data *data)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -355,7 +355,7 @@ static void mc_msi_bottom_irq_ack(struct
+ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+ }
+
+-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ phys_addr_t addr = port->msi.vector_phy;
+@@ -368,21 +368,23 @@ static void mc_compose_msi_msg(struct ir
+ (int)data->hwirq, msg->address_hi, msg->address_lo);
+ }
+
+-static int mc_msi_set_affinity(struct irq_data *irq_data,
+- const struct cpumask *mask, bool force)
++static int plda_msi_set_affinity(struct irq_data *irq_data,
++ const struct cpumask *mask, bool force)
+ {
+ return -EINVAL;
+ }
+
+-static struct irq_chip mc_msi_bottom_irq_chip = {
+- .name = "Microchip MSI",
+- .irq_ack = mc_msi_bottom_irq_ack,
+- .irq_compose_msi_msg = mc_compose_msi_msg,
+- .irq_set_affinity = mc_msi_set_affinity,
++static struct irq_chip plda_msi_bottom_irq_chip = {
++ .name = "PLDA MSI",
++ .irq_ack = plda_msi_bottom_irq_ack,
++ .irq_compose_msi_msg = plda_compose_msi_msg,
++ .irq_set_affinity = plda_msi_set_affinity,
+ };
+
+-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+- unsigned int nr_irqs, void *args)
++static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
++ unsigned int virq,
++ unsigned int nr_irqs,
++ void *args)
+ {
+ struct plda_pcie_rp *port = domain->host_data;
+ struct plda_msi *msi = &port->msi;
+@@ -397,7 +399,7 @@ static int mc_irq_msi_domain_alloc(struc
+
+ set_bit(bit, msi->used);
+
+- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
+ domain->host_data, handle_edge_irq, NULL, NULL);
+
+ mutex_unlock(&msi->lock);
+@@ -405,8 +407,9 @@ static int mc_irq_msi_domain_alloc(struc
+ return 0;
+ }
+
+-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+- unsigned int nr_irqs)
++static void plda_irq_msi_domain_free(struct irq_domain *domain,
++ unsigned int virq,
++ unsigned int nr_irqs)
+ {
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
+@@ -423,24 +426,24 @@ static void mc_irq_msi_domain_free(struc
+ }
+
+ static const struct irq_domain_ops msi_domain_ops = {
+- .alloc = mc_irq_msi_domain_alloc,
+- .free = mc_irq_msi_domain_free,
++ .alloc = plda_irq_msi_domain_alloc,
++ .free = plda_irq_msi_domain_free,
+ };
+
+-static struct irq_chip mc_msi_irq_chip = {
+- .name = "Microchip PCIe MSI",
++static struct irq_chip plda_msi_irq_chip = {
++ .name = "PLDA PCIe MSI",
+ .irq_ack = irq_chip_ack_parent,
+ .irq_mask = pci_msi_mask_irq,
+ .irq_unmask = pci_msi_unmask_irq,
+ };
+
+-static struct msi_domain_info mc_msi_domain_info = {
++static struct msi_domain_info plda_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSIX),
+- .chip = &mc_msi_irq_chip,
++ .chip = &plda_msi_irq_chip,
+ };
+
+-static int mc_allocate_msi_domains(struct plda_pcie_rp *port)
++static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
+ {
+ struct device *dev = port->dev;
+ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+@@ -455,7 +458,8 @@ static int mc_allocate_msi_domains(struc
+ return -ENOMEM;
+ }
+
+- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
++ msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++ &plda_msi_domain_info,
+ msi->dev_domain);
+ if (!msi->msi_domain) {
+ dev_err(dev, "failed to create MSI domain\n");
+@@ -466,7 +470,7 @@ static int mc_allocate_msi_domains(struc
+ return 0;
+ }
+
+-static void mc_handle_intx(struct irq_desc *desc)
++static void plda_handle_intx(struct irq_desc *desc)
+ {
+ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -493,7 +497,7 @@ static void mc_handle_intx(struct irq_de
+ chained_irq_exit(chip, desc);
+ }
+
+-static void mc_ack_intx_irq(struct irq_data *data)
++static void plda_ack_intx_irq(struct irq_data *data)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -502,7 +506,7 @@ static void mc_ack_intx_irq(struct irq_d
+ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+ }
+
+-static void mc_mask_intx_irq(struct irq_data *data)
++static void plda_mask_intx_irq(struct irq_data *data)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -517,7 +521,7 @@ static void mc_mask_intx_irq(struct irq_
+ raw_spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+-static void mc_unmask_intx_irq(struct irq_data *data)
++static void plda_unmask_intx_irq(struct irq_data *data)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+ void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -532,24 +536,24 @@ static void mc_unmask_intx_irq(struct ir
+ raw_spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+-static struct irq_chip mc_intx_irq_chip = {
+- .name = "Microchip PCIe INTx",
+- .irq_ack = mc_ack_intx_irq,
+- .irq_mask = mc_mask_intx_irq,
+- .irq_unmask = mc_unmask_intx_irq,
++static struct irq_chip plda_intx_irq_chip = {
++ .name = "PLDA PCIe INTx",
++ .irq_ack = plda_ack_intx_irq,
++ .irq_mask = plda_mask_intx_irq,
++ .irq_unmask = plda_unmask_intx_irq,
+ };
+
+-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
+ {
+- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+
+ return 0;
+ }
+
+ static const struct irq_domain_ops intx_domain_ops = {
+- .map = mc_pcie_intx_map,
++ .map = plda_pcie_intx_map,
+ };
+
+ static inline u32 reg_to_event(u32 reg, struct event_map field)
+@@ -609,7 +613,7 @@ static u32 local_events(struct mc_pcie *
+ return val;
+ }
+
+-static u32 get_events(struct plda_pcie_rp *port)
++static u32 mc_get_events(struct plda_pcie_rp *port)
+ {
+ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+ u32 events = 0;
+@@ -638,7 +642,7 @@ static irqreturn_t mc_event_handler(int
+ return IRQ_HANDLED;
+ }
+
+-static void mc_handle_event(struct irq_desc *desc)
++static void plda_handle_event(struct irq_desc *desc)
+ {
+ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+ unsigned long events;
+@@ -647,7 +651,7 @@ static void mc_handle_event(struct irq_d
+
+ chained_irq_enter(chip, desc);
+
+- events = get_events(port);
++ events = mc_get_events(port);
+
+ for_each_set_bit(bit, &events, NUM_EVENTS)
+ generic_handle_domain_irq(port->event_domain, bit);
+@@ -741,8 +745,8 @@ static struct irq_chip mc_event_irq_chip
+ .irq_unmask = mc_unmask_event_irq,
+ };
+
+-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
+ {
+ irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+@@ -750,8 +754,8 @@ static int mc_pcie_event_map(struct irq_
+ return 0;
+ }
+
+-static const struct irq_domain_ops event_domain_ops = {
+- .map = mc_pcie_event_map,
++static const struct irq_domain_ops plda_event_domain_ops = {
++ .map = plda_pcie_event_map,
+ };
+
+ static inline void mc_pcie_deinit_clk(void *data)
+@@ -799,7 +803,7 @@ static int mc_pcie_init_clks(struct devi
+ return 0;
+ }
+
+-static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port)
++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+ struct device *dev = port->dev;
+ struct device_node *node = dev->of_node;
+@@ -813,7 +817,8 @@ static int mc_pcie_init_irq_domains(stru
+ }
+
+ port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
+- &event_domain_ops, port);
++ &plda_event_domain_ops,
++ port);
+ if (!port->event_domain) {
+ dev_err(dev, "failed to get event domain\n");
+ of_node_put(pcie_intc_node);
+@@ -835,7 +840,7 @@ static int mc_pcie_init_irq_domains(stru
+ of_node_put(pcie_intc_node);
+ raw_spin_lock_init(&port->lock);
+
+- return mc_allocate_msi_domains(port);
++ return plda_allocate_msi_domains(port);
+ }
+
+ static inline void mc_clear_secs(struct mc_pcie *port)
+@@ -898,14 +903,14 @@ static void mc_disable_interrupts(struct
+ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+
+-static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
++static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
+ {
+ struct device *dev = &pdev->dev;
+ int irq;
+ int i, intx_irq, msi_irq, event_irq;
+ int ret;
+
+- ret = mc_pcie_init_irq_domains(port);
++ ret = plda_pcie_init_irq_domains(port);
+ if (ret) {
+ dev_err(dev, "failed creating IRQ domains\n");
+ return ret;
+@@ -938,7 +943,7 @@ static int mc_init_interrupts(struct pla
+ }
+
+ /* Plug the INTx chained handler */
+- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+
+ msi_irq = irq_create_mapping(port->event_domain,
+ EVENT_LOCAL_PM_MSI_INT_MSI);
+@@ -946,10 +951,10 @@ static int mc_init_interrupts(struct pla
+ return -ENXIO;
+
+ /* Plug the MSI chained handler */
+- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
+
+ /* Plug the main event chained handler */
+- irq_set_chained_handler_and_data(irq, mc_handle_event, port);
++ irq_set_chained_handler_and_data(irq, plda_handle_event, port);
+
+ return 0;
+ }
+@@ -977,7 +982,7 @@ static int mc_platform_init(struct pci_c
+ return ret;
+
+ /* Address translation is up; safe to enable interrupts */
+- ret = mc_init_interrupts(pdev, &port->plda);
++ ret = plda_init_interrupts(pdev, &port->plda);
+ if (ret)
+ return ret;
+
--- /dev/null
+From ab04dadb45a4150c9fd55b97fdd7397f4739a629 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:01 +0800
+Subject: [PATCH 024/116] PCI: microchip: Add num_events field to struct
+ plda_pcie_rp
+
+The number of events is different across platforms. In order to share
+interrupt processing code, add a variable that defines the number of
+events so that it can be set per-platform instead of hardcoding it.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 8 +++++---
+ drivers/pci/controller/plda/pcie-plda.h | 1 +
+ 2 files changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -653,7 +653,7 @@ static void plda_handle_event(struct irq
+
+ events = mc_get_events(port);
+
+- for_each_set_bit(bit, &events, NUM_EVENTS)
++ for_each_set_bit(bit, &events, port->num_events)
+ generic_handle_domain_irq(port->event_domain, bit);
+
+ chained_irq_exit(chip, desc);
+@@ -816,7 +816,8 @@ static int plda_pcie_init_irq_domains(st
+ return -EINVAL;
+ }
+
+- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
++ port->event_domain = irq_domain_add_linear(pcie_intc_node,
++ port->num_events,
+ &plda_event_domain_ops,
+ port);
+ if (!port->event_domain) {
+@@ -920,7 +921,7 @@ static int plda_init_interrupts(struct p
+ if (irq < 0)
+ return -ENODEV;
+
+- for (i = 0; i < NUM_EVENTS; i++) {
++ for (i = 0; i < port->num_events; i++) {
+ event_irq = irq_create_mapping(port->event_domain, i);
+ if (!event_irq) {
+ dev_err(dev, "failed to map hwirq %d\n", i);
+@@ -1012,6 +1013,7 @@ static int mc_host_probe(struct platform
+
+ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+ plda->bridge_addr = bridge_base_addr;
++ plda->num_events = NUM_EVENTS;
+
+ /* Allow enabling MSI by disabling MSI-X */
+ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -124,6 +124,7 @@ struct plda_pcie_rp {
+ raw_spinlock_t lock;
+ struct plda_msi msi;
+ void __iomem *bridge_addr;
++ int num_events;
+ };
+
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
--- /dev/null
+From 9f202f211cc79eefecbb03715c884e54eb95a62c Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:02 +0800
+Subject: [PATCH 025/116] PCI: microchip: Add request_event_irq() callback
+ function
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller. Microchip Polarfire PCIe add some PCIe interrupts base on
+PLDA IP interrupt controller.
+
+Microchip Polarfire PCIe additional intrerrupts:
+EVENT_PCIE_L2_EXIT
+EVENT_PCIE_HOTRST_EXIT
+EVENT_PCIE_DLUP_EXIT
+EVENT_SEC_TX_RAM_SEC_ERR
+EVENT_SEC_RX_RAM_SEC_ERR
+....
+
+Both codes of register interrupts and mc_event_handler() contain
+additional interrupts symbol names, these can not be re-used. So add a
+new plda_event_handler() functions, which implements PLDA interrupt
+defalt handler. Add request_event_irq() callback function to
+compat Microchip Polorfire PCIe additional interrupts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 31 ++++++++++++++++---
+ drivers/pci/controller/plda/pcie-plda.h | 5 +++
+ 2 files changed, 32 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -642,6 +642,11 @@ static irqreturn_t mc_event_handler(int
+ return IRQ_HANDLED;
+ }
+
++static irqreturn_t plda_event_handler(int irq, void *dev_id)
++{
++ return IRQ_HANDLED;
++}
++
+ static void plda_handle_event(struct irq_desc *desc)
+ {
+ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+@@ -803,6 +808,17 @@ static int mc_pcie_init_clks(struct devi
+ return 0;
+ }
+
++static int mc_request_event_irq(struct plda_pcie_rp *plda, int event_irq,
++ int event)
++{
++ return devm_request_irq(plda->dev, event_irq, mc_event_handler,
++ 0, event_cause[event].sym, plda);
++}
++
++static const struct plda_event mc_event = {
++ .request_event_irq = mc_request_event_irq,
++};
++
+ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+ struct device *dev = port->dev;
+@@ -904,7 +920,9 @@ static void mc_disable_interrupts(struct
+ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+
+-static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
++static int plda_init_interrupts(struct platform_device *pdev,
++ struct plda_pcie_rp *port,
++ const struct plda_event *event)
+ {
+ struct device *dev = &pdev->dev;
+ int irq;
+@@ -928,8 +946,13 @@ static int plda_init_interrupts(struct p
+ return -ENXIO;
+ }
+
+- ret = devm_request_irq(dev, event_irq, mc_event_handler,
+- 0, event_cause[i].sym, port);
++ if (event->request_event_irq)
++ ret = event->request_event_irq(port, event_irq, i);
++ else
++ ret = devm_request_irq(dev, event_irq,
++ plda_event_handler,
++ 0, NULL, port);
++
+ if (ret) {
+ dev_err(dev, "failed to request IRQ %d\n", event_irq);
+ return ret;
+@@ -983,7 +1006,7 @@ static int mc_platform_init(struct pci_c
+ return ret;
+
+ /* Address translation is up; safe to enable interrupts */
+- ret = plda_init_interrupts(pdev, &port->plda);
++ ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -127,6 +127,11 @@ struct plda_pcie_rp {
+ int num_events;
+ };
+
++struct plda_event {
++ int (*request_event_irq)(struct plda_pcie_rp *pcie,
++ int event_irq, int event);
++};
++
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+ phys_addr_t axi_addr, phys_addr_t pci_addr,
+ size_t size);
--- /dev/null
+From 3cdc20d9cc029ba9444be111bf4e55fd5331ccbe Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:03 +0800
+Subject: [PATCH 026/116] PCI: microchip: Add INTx and MSI event num to struct
+ plda_event
+
+The INTx and MSI interrupt event num is different in Microchip and
+StarFive platform.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 6 ++++--
+ drivers/pci/controller/plda/pcie-plda.h | 2 ++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -817,6 +817,8 @@ static int mc_request_event_irq(struct p
+
+ static const struct plda_event mc_event = {
+ .request_event_irq = mc_request_event_irq,
++ .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX,
++ .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI,
+ };
+
+ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+@@ -960,7 +962,7 @@ static int plda_init_interrupts(struct p
+ }
+
+ intx_irq = irq_create_mapping(port->event_domain,
+- EVENT_LOCAL_PM_MSI_INT_INTX);
++ event->intx_event);
+ if (!intx_irq) {
+ dev_err(dev, "failed to map INTx interrupt\n");
+ return -ENXIO;
+@@ -970,7 +972,7 @@ static int plda_init_interrupts(struct p
+ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+
+ msi_irq = irq_create_mapping(port->event_domain,
+- EVENT_LOCAL_PM_MSI_INT_MSI);
++ event->msi_event);
+ if (!msi_irq)
+ return -ENXIO;
+
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -130,6 +130,8 @@ struct plda_pcie_rp {
+ struct plda_event {
+ int (*request_event_irq)(struct plda_pcie_rp *pcie,
+ int event_irq, int event);
++ int intx_event;
++ int msi_event;
+ };
+
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
--- /dev/null
+From b4a38ef87661f21fe2fb3e085ae98f25f78aaf99 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:04 +0800
+Subject: [PATCH 027/116] PCI: microchip: Add get_events() callback and add
+ PLDA get_event()
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller.
+
+PolarFire implements its own PCIe interrupts, additional to the regular
+PCIe interrupts, due to lack of an MSI controller, so the interrupt to
+event number mapping is different to the PLDA regular interrupts,
+necessitating a custom get_events() implementation.
+
+Microchip Polarfire PCIe additional intrerrupts:
+EVENT_PCIE_L2_EXIT
+EVENT_PCIE_HOTRST_EXIT
+EVENT_PCIE_DLUP_EXIT
+EVENT_SEC_TX_RAM_SEC_ERR
+EVENT_SEC_RX_RAM_SEC_ERR
+....
+
+plda_get_events() adds interrupt register to PLDA local event num mapping
+codes. All The PLDA interrupts can be seen in new added graph.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 35 ++++++++++++++++++-
+ drivers/pci/controller/plda/pcie-plda.h | 32 +++++++++++++++++
+ 2 files changed, 66 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -626,6 +626,26 @@ static u32 mc_get_events(struct plda_pci
+ return events;
+ }
+
++static u32 plda_get_events(struct plda_pcie_rp *port)
++{
++ u32 events, val, origin;
++
++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
++
++ /* MSI event and sys events */
++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
++
++ /* INTx events */
++ if (origin & PM_MSI_INT_INTX_MASK)
++ events |= BIT(PM_MSI_INT_INTX_SHIFT);
++
++ /* remains are same with register */
++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
++
++ return events;
++}
++
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+ struct plda_pcie_rp *port = dev_id;
+@@ -656,7 +676,7 @@ static void plda_handle_event(struct irq
+
+ chained_irq_enter(chip, desc);
+
+- events = mc_get_events(port);
++ events = port->event_ops->get_events(port);
+
+ for_each_set_bit(bit, &events, port->num_events)
+ generic_handle_domain_irq(port->event_domain, bit);
+@@ -750,6 +770,10 @@ static struct irq_chip mc_event_irq_chip
+ .irq_unmask = mc_unmask_event_irq,
+ };
+
++static const struct plda_event_ops plda_event_ops = {
++ .get_events = plda_get_events,
++};
++
+ static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+ {
+@@ -815,6 +839,10 @@ static int mc_request_event_irq(struct p
+ 0, event_cause[event].sym, plda);
+ }
+
++static const struct plda_event_ops mc_event_ops = {
++ .get_events = mc_get_events,
++};
++
+ static const struct plda_event mc_event = {
+ .request_event_irq = mc_request_event_irq,
+ .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX,
+@@ -931,6 +959,9 @@ static int plda_init_interrupts(struct p
+ int i, intx_irq, msi_irq, event_irq;
+ int ret;
+
++ if (!port->event_ops)
++ port->event_ops = &plda_event_ops;
++
+ ret = plda_pcie_init_irq_domains(port);
+ if (ret) {
+ dev_err(dev, "failed creating IRQ domains\n");
+@@ -1007,6 +1038,8 @@ static int mc_platform_init(struct pci_c
+ if (ret)
+ return ret;
+
++ port->plda.event_ops = &mc_event_ops;
++
+ /* Address translation is up; safe to enable interrupts */
+ ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+ if (ret)
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -58,6 +58,7 @@
+ #define PM_MSI_INT_EVENTS_SHIFT 30
+ #define PM_MSI_INT_SYS_ERR_MASK 0x80000000u
+ #define PM_MSI_INT_SYS_ERR_SHIFT 31
++#define SYS_AND_MSI_MASK GENMASK(31, 28)
+ #define NUM_LOCAL_EVENTS 15
+ #define ISTATUS_LOCAL 0x184
+ #define IMASK_HOST 0x188
+@@ -108,6 +109,36 @@ enum plda_int_event {
+
+ #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
+
++/*
++ * PLDA interrupt register
++ *
++ * 31 27 23 15 7 0
++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+
++ * |12|11|10|9| intx |7|6|5|4|3|2|1|0| DMA error | DMA end |
++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+
++ * bit 0-7 DMA interrupt end : reserved for vendor implement
++ * bit 8-15 DMA error : reserved for vendor implement
++ * 0: AXI post error (PLDA_AXI_POST_ERR)
++ * 1: AXI fetch error (PLDA_AXI_FETCH_ERR)
++ * 2: AXI discard error (PLDA_AXI_DISCARD_ERR)
++ * 3: AXI doorbell (PLDA_PCIE_DOORBELL)
++ * 4: PCIe post error (PLDA_PCIE_POST_ERR)
++ * 5: PCIe fetch error (PLDA_PCIE_FETCH_ERR)
++ * 6: PCIe discard error (PLDA_PCIE_DISCARD_ERR)
++ * 7: PCIe doorbell (PLDA_PCIE_DOORBELL)
++ * 8: 4 INTx interruts (PLDA_INTX)
++ * 9: MSI interrupt (PLDA_MSI)
++ * 10: AER event (PLDA_AER_EVENT)
++ * 11: PM/LTR/Hotplug (PLDA_MISC_EVENTS)
++ * 12: System error (PLDA_SYS_ERR)
++ */
++
++struct plda_pcie_rp;
++
++struct plda_event_ops {
++ u32 (*get_events)(struct plda_pcie_rp *pcie);
++};
++
+ struct plda_msi {
+ struct mutex lock; /* Protect used bitmap */
+ struct irq_domain *msi_domain;
+@@ -123,6 +154,7 @@ struct plda_pcie_rp {
+ struct irq_domain *event_domain;
+ raw_spinlock_t lock;
+ struct plda_msi msi;
++ const struct plda_event_ops *event_ops;
+ void __iomem *bridge_addr;
+ int num_events;
+ };
--- /dev/null
+From 229ea8e7b674eb5c9bc4f70d43df1bd02a79862a Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:05 +0800
+Subject: [PATCH 028/116] PCI: microchip: Add event irqchip field to host port
+ and add PLDA irqchip
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller.
+
+Microchip PolarFire PCIE event IRQs includes PLDA interrupts and
+Polarfire their own interrupts. The interrupt irqchip ops includes
+ack/mask/unmask interrupt ops, which will write correct registers.
+Microchip Polarfire PCIe additional interrupts require to write Polarfire
+SoC self-defined registers. So Microchip PCIe event irqchip ops can not
+be re-used.
+
+To support PLDA its own event IRQ process, implements PLDA irqchip ops and
+add event irqchip field to struct pcie_plda_rp.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 66 ++++++++++++++++++-
+ drivers/pci/controller/plda/pcie-plda.h | 5 +-
+ 2 files changed, 69 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -770,6 +770,64 @@ static struct irq_chip mc_event_irq_chip
+ .irq_unmask = mc_unmask_event_irq,
+ };
+
++static u32 plda_hwirq_to_mask(int hwirq)
++{
++ u32 mask;
++
++ /* hwirq 23 - 0 are the same with register */
++ if (hwirq < EVENT_PM_MSI_INT_INTX)
++ mask = BIT(hwirq);
++ else if (hwirq == EVENT_PM_MSI_INT_INTX)
++ mask = PM_MSI_INT_INTX_MASK;
++ else
++ mask = BIT(hwirq + PCI_NUM_INTX - 1);
++
++ return mask;
++}
++
++static void plda_ack_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++
++ writel_relaxed(plda_hwirq_to_mask(data->hwirq),
++ port->bridge_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ u32 mask, val;
++
++ mask = plda_hwirq_to_mask(data->hwirq);
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++ val &= ~mask;
++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++ raw_spin_unlock(&port->lock);
++}
++
++static void plda_unmask_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ u32 mask, val;
++
++ mask = plda_hwirq_to_mask(data->hwirq);
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++ val |= mask;
++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++ raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip plda_event_irq_chip = {
++ .name = "PLDA PCIe EVENT",
++ .irq_ack = plda_ack_event_irq,
++ .irq_mask = plda_mask_event_irq,
++ .irq_unmask = plda_unmask_event_irq,
++};
++
+ static const struct plda_event_ops plda_event_ops = {
+ .get_events = plda_get_events,
+ };
+@@ -777,7 +835,9 @@ static const struct plda_event_ops plda_
+ static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+ {
+- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
++ struct plda_pcie_rp *port = (void *)domain->host_data;
++
++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+
+ return 0;
+@@ -962,6 +1022,9 @@ static int plda_init_interrupts(struct p
+ if (!port->event_ops)
+ port->event_ops = &plda_event_ops;
+
++ if (!port->event_irq_chip)
++ port->event_irq_chip = &plda_event_irq_chip;
++
+ ret = plda_pcie_init_irq_domains(port);
+ if (ret) {
+ dev_err(dev, "failed creating IRQ domains\n");
+@@ -1039,6 +1102,7 @@ static int mc_platform_init(struct pci_c
+ return ret;
+
+ port->plda.event_ops = &mc_event_ops;
++ port->plda.event_irq_chip = &mc_event_irq_chip;
+
+ /* Address translation is up; safe to enable interrupts */
+ ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -107,7 +107,9 @@ enum plda_int_event {
+
+ #define PLDA_NUM_DMA_EVENTS 16
+
+-#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++#define EVENT_PM_MSI_INT_INTX (PLDA_NUM_DMA_EVENTS + PLDA_INTX)
++#define EVENT_PM_MSI_INT_MSI (PLDA_NUM_DMA_EVENTS + PLDA_MSI)
++#define PLDA_MAX_EVENT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
+
+ /*
+ * PLDA interrupt register
+@@ -155,6 +157,7 @@ struct plda_pcie_rp {
+ raw_spinlock_t lock;
+ struct plda_msi msi;
+ const struct plda_event_ops *event_ops;
++ const struct irq_chip *event_irq_chip;
+ void __iomem *bridge_addr;
+ int num_events;
+ };
--- /dev/null
+From 6be452d8e61594790ae57b282a612ec0df473e61 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:06 +0800
+Subject: [PATCH 029/116] PCI: microchip: Move IRQ functions to
+ pcie-plda-host.c
+
+Move IRQ related functions to pcie-plda-host.c for re-use these codes.
+Now Refactoring codes complete.
+
+Including MSI, INTx, event interrupts and IRQ init functions.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 467 -----------------
+ drivers/pci/controller/plda/pcie-plda-host.c | 472 ++++++++++++++++++
+ drivers/pci/controller/plda/pcie-plda.h | 3 +
+ 3 files changed, 475 insertions(+), 467 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -318,244 +318,6 @@ static void mc_pcie_enable_msi(struct mc
+ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+ }
+
+-static void plda_handle_msi(struct irq_desc *desc)
+-{
+- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+- struct device *dev = port->dev;
+- struct plda_msi *msi = &port->msi;
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- unsigned long status;
+- u32 bit;
+- int ret;
+-
+- chained_irq_enter(chip, desc);
+-
+- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+- if (status & PM_MSI_INT_MSI_MASK) {
+- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
+- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+- for_each_set_bit(bit, &status, msi->num_vectors) {
+- ret = generic_handle_domain_irq(msi->dev_domain, bit);
+- if (ret)
+- dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
+- bit);
+- }
+- }
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+-static void plda_msi_bottom_irq_ack(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- u32 bitpos = data->hwirq;
+-
+- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+-}
+-
+-static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- phys_addr_t addr = port->msi.vector_phy;
+-
+- msg->address_lo = lower_32_bits(addr);
+- msg->address_hi = upper_32_bits(addr);
+- msg->data = data->hwirq;
+-
+- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
+- (int)data->hwirq, msg->address_hi, msg->address_lo);
+-}
+-
+-static int plda_msi_set_affinity(struct irq_data *irq_data,
+- const struct cpumask *mask, bool force)
+-{
+- return -EINVAL;
+-}
+-
+-static struct irq_chip plda_msi_bottom_irq_chip = {
+- .name = "PLDA MSI",
+- .irq_ack = plda_msi_bottom_irq_ack,
+- .irq_compose_msi_msg = plda_compose_msi_msg,
+- .irq_set_affinity = plda_msi_set_affinity,
+-};
+-
+-static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
+- unsigned int virq,
+- unsigned int nr_irqs,
+- void *args)
+-{
+- struct plda_pcie_rp *port = domain->host_data;
+- struct plda_msi *msi = &port->msi;
+- unsigned long bit;
+-
+- mutex_lock(&msi->lock);
+- bit = find_first_zero_bit(msi->used, msi->num_vectors);
+- if (bit >= msi->num_vectors) {
+- mutex_unlock(&msi->lock);
+- return -ENOSPC;
+- }
+-
+- set_bit(bit, msi->used);
+-
+- irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
+- domain->host_data, handle_edge_irq, NULL, NULL);
+-
+- mutex_unlock(&msi->lock);
+-
+- return 0;
+-}
+-
+-static void plda_irq_msi_domain_free(struct irq_domain *domain,
+- unsigned int virq,
+- unsigned int nr_irqs)
+-{
+- struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
+- struct plda_msi *msi = &port->msi;
+-
+- mutex_lock(&msi->lock);
+-
+- if (test_bit(d->hwirq, msi->used))
+- __clear_bit(d->hwirq, msi->used);
+- else
+- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
+-
+- mutex_unlock(&msi->lock);
+-}
+-
+-static const struct irq_domain_ops msi_domain_ops = {
+- .alloc = plda_irq_msi_domain_alloc,
+- .free = plda_irq_msi_domain_free,
+-};
+-
+-static struct irq_chip plda_msi_irq_chip = {
+- .name = "PLDA PCIe MSI",
+- .irq_ack = irq_chip_ack_parent,
+- .irq_mask = pci_msi_mask_irq,
+- .irq_unmask = pci_msi_unmask_irq,
+-};
+-
+-static struct msi_domain_info plda_msi_domain_info = {
+- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+- MSI_FLAG_PCI_MSIX),
+- .chip = &plda_msi_irq_chip,
+-};
+-
+-static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
+-{
+- struct device *dev = port->dev;
+- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+- struct plda_msi *msi = &port->msi;
+-
+- mutex_init(&port->msi.lock);
+-
+- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
+- &msi_domain_ops, port);
+- if (!msi->dev_domain) {
+- dev_err(dev, "failed to create IRQ domain\n");
+- return -ENOMEM;
+- }
+-
+- msi->msi_domain = pci_msi_create_irq_domain(fwnode,
+- &plda_msi_domain_info,
+- msi->dev_domain);
+- if (!msi->msi_domain) {
+- dev_err(dev, "failed to create MSI domain\n");
+- irq_domain_remove(msi->dev_domain);
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-static void plda_handle_intx(struct irq_desc *desc)
+-{
+- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+- struct device *dev = port->dev;
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- unsigned long status;
+- u32 bit;
+- int ret;
+-
+- chained_irq_enter(chip, desc);
+-
+- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+- if (status & PM_MSI_INT_INTX_MASK) {
+- status &= PM_MSI_INT_INTX_MASK;
+- status >>= PM_MSI_INT_INTX_SHIFT;
+- for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+- ret = generic_handle_domain_irq(port->intx_domain, bit);
+- if (ret)
+- dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
+- bit);
+- }
+- }
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+-static void plda_ack_intx_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-
+- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+-}
+-
+-static void plda_mask_intx_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- unsigned long flags;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+- u32 val;
+-
+- raw_spin_lock_irqsave(&port->lock, flags);
+- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+- val &= ~mask;
+- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+- raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static void plda_unmask_intx_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- void __iomem *bridge_base_addr = port->bridge_addr;
+- unsigned long flags;
+- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+- u32 val;
+-
+- raw_spin_lock_irqsave(&port->lock, flags);
+- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+- val |= mask;
+- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+- raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static struct irq_chip plda_intx_irq_chip = {
+- .name = "PLDA PCIe INTx",
+- .irq_ack = plda_ack_intx_irq,
+- .irq_mask = plda_mask_intx_irq,
+- .irq_unmask = plda_unmask_intx_irq,
+-};
+-
+-static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
+-{
+- irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
+- irq_set_chip_data(irq, domain->host_data);
+-
+- return 0;
+-}
+-
+-static const struct irq_domain_ops intx_domain_ops = {
+- .map = plda_pcie_intx_map,
+-};
+-
+ static inline u32 reg_to_event(u32 reg, struct event_map field)
+ {
+ return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
+@@ -626,26 +388,6 @@ static u32 mc_get_events(struct plda_pci
+ return events;
+ }
+
+-static u32 plda_get_events(struct plda_pcie_rp *port)
+-{
+- u32 events, val, origin;
+-
+- origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
+-
+- /* MSI event and sys events */
+- val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
+- events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
+-
+- /* INTx events */
+- if (origin & PM_MSI_INT_INTX_MASK)
+- events |= BIT(PM_MSI_INT_INTX_SHIFT);
+-
+- /* remains are same with register */
+- events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
+-
+- return events;
+-}
+-
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+ struct plda_pcie_rp *port = dev_id;
+@@ -662,28 +404,6 @@ static irqreturn_t mc_event_handler(int
+ return IRQ_HANDLED;
+ }
+
+-static irqreturn_t plda_event_handler(int irq, void *dev_id)
+-{
+- return IRQ_HANDLED;
+-}
+-
+-static void plda_handle_event(struct irq_desc *desc)
+-{
+- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+- unsigned long events;
+- u32 bit;
+- struct irq_chip *chip = irq_desc_get_chip(desc);
+-
+- chained_irq_enter(chip, desc);
+-
+- events = port->event_ops->get_events(port);
+-
+- for_each_set_bit(bit, &events, port->num_events)
+- generic_handle_domain_irq(port->event_domain, bit);
+-
+- chained_irq_exit(chip, desc);
+-}
+-
+ static void mc_ack_event_irq(struct irq_data *data)
+ {
+ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+@@ -770,83 +490,6 @@ static struct irq_chip mc_event_irq_chip
+ .irq_unmask = mc_unmask_event_irq,
+ };
+
+-static u32 plda_hwirq_to_mask(int hwirq)
+-{
+- u32 mask;
+-
+- /* hwirq 23 - 0 are the same with register */
+- if (hwirq < EVENT_PM_MSI_INT_INTX)
+- mask = BIT(hwirq);
+- else if (hwirq == EVENT_PM_MSI_INT_INTX)
+- mask = PM_MSI_INT_INTX_MASK;
+- else
+- mask = BIT(hwirq + PCI_NUM_INTX - 1);
+-
+- return mask;
+-}
+-
+-static void plda_ack_event_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-
+- writel_relaxed(plda_hwirq_to_mask(data->hwirq),
+- port->bridge_addr + ISTATUS_LOCAL);
+-}
+-
+-static void plda_mask_event_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- u32 mask, val;
+-
+- mask = plda_hwirq_to_mask(data->hwirq);
+-
+- raw_spin_lock(&port->lock);
+- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
+- val &= ~mask;
+- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
+- raw_spin_unlock(&port->lock);
+-}
+-
+-static void plda_unmask_event_irq(struct irq_data *data)
+-{
+- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+- u32 mask, val;
+-
+- mask = plda_hwirq_to_mask(data->hwirq);
+-
+- raw_spin_lock(&port->lock);
+- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
+- val |= mask;
+- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
+- raw_spin_unlock(&port->lock);
+-}
+-
+-static struct irq_chip plda_event_irq_chip = {
+- .name = "PLDA PCIe EVENT",
+- .irq_ack = plda_ack_event_irq,
+- .irq_mask = plda_mask_event_irq,
+- .irq_unmask = plda_unmask_event_irq,
+-};
+-
+-static const struct plda_event_ops plda_event_ops = {
+- .get_events = plda_get_events,
+-};
+-
+-static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+- irq_hw_number_t hwirq)
+-{
+- struct plda_pcie_rp *port = (void *)domain->host_data;
+-
+- irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
+- irq_set_chip_data(irq, domain->host_data);
+-
+- return 0;
+-}
+-
+-static const struct irq_domain_ops plda_event_domain_ops = {
+- .map = plda_pcie_event_map,
+-};
+-
+ static inline void mc_pcie_deinit_clk(void *data)
+ {
+ struct clk *clk = data;
+@@ -909,47 +552,6 @@ static const struct plda_event mc_event
+ .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI,
+ };
+
+-static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+-{
+- struct device *dev = port->dev;
+- struct device_node *node = dev->of_node;
+- struct device_node *pcie_intc_node;
+-
+- /* Setup INTx */
+- pcie_intc_node = of_get_next_child(node, NULL);
+- if (!pcie_intc_node) {
+- dev_err(dev, "failed to find PCIe Intc node\n");
+- return -EINVAL;
+- }
+-
+- port->event_domain = irq_domain_add_linear(pcie_intc_node,
+- port->num_events,
+- &plda_event_domain_ops,
+- port);
+- if (!port->event_domain) {
+- dev_err(dev, "failed to get event domain\n");
+- of_node_put(pcie_intc_node);
+- return -ENOMEM;
+- }
+-
+- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
+-
+- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+- &intx_domain_ops, port);
+- if (!port->intx_domain) {
+- dev_err(dev, "failed to get an INTx IRQ domain\n");
+- of_node_put(pcie_intc_node);
+- return -ENOMEM;
+- }
+-
+- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
+-
+- of_node_put(pcie_intc_node);
+- raw_spin_lock_init(&port->lock);
+-
+- return plda_allocate_msi_domains(port);
+-}
+-
+ static inline void mc_clear_secs(struct mc_pcie *port)
+ {
+ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+@@ -1010,75 +612,6 @@ static void mc_disable_interrupts(struct
+ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+
+-static int plda_init_interrupts(struct platform_device *pdev,
+- struct plda_pcie_rp *port,
+- const struct plda_event *event)
+-{
+- struct device *dev = &pdev->dev;
+- int irq;
+- int i, intx_irq, msi_irq, event_irq;
+- int ret;
+-
+- if (!port->event_ops)
+- port->event_ops = &plda_event_ops;
+-
+- if (!port->event_irq_chip)
+- port->event_irq_chip = &plda_event_irq_chip;
+-
+- ret = plda_pcie_init_irq_domains(port);
+- if (ret) {
+- dev_err(dev, "failed creating IRQ domains\n");
+- return ret;
+- }
+-
+- irq = platform_get_irq(pdev, 0);
+- if (irq < 0)
+- return -ENODEV;
+-
+- for (i = 0; i < port->num_events; i++) {
+- event_irq = irq_create_mapping(port->event_domain, i);
+- if (!event_irq) {
+- dev_err(dev, "failed to map hwirq %d\n", i);
+- return -ENXIO;
+- }
+-
+- if (event->request_event_irq)
+- ret = event->request_event_irq(port, event_irq, i);
+- else
+- ret = devm_request_irq(dev, event_irq,
+- plda_event_handler,
+- 0, NULL, port);
+-
+- if (ret) {
+- dev_err(dev, "failed to request IRQ %d\n", event_irq);
+- return ret;
+- }
+- }
+-
+- intx_irq = irq_create_mapping(port->event_domain,
+- event->intx_event);
+- if (!intx_irq) {
+- dev_err(dev, "failed to map INTx interrupt\n");
+- return -ENXIO;
+- }
+-
+- /* Plug the INTx chained handler */
+- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+-
+- msi_irq = irq_create_mapping(port->event_domain,
+- event->msi_event);
+- if (!msi_irq)
+- return -ENXIO;
+-
+- /* Plug the MSI chained handler */
+- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
+-
+- /* Plug the main event chained handler */
+- irq_set_chained_handler_and_data(irq, plda_handle_event, port);
+-
+- return 0;
+-}
+-
+ static int mc_platform_init(struct pci_config_window *cfg)
+ {
+ struct device *dev = cfg->parent;
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -7,11 +7,483 @@
+ * Author: Daire McNamara <daire.mcnamara@microchip.com>
+ */
+
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/msi.h>
+ #include <linux/pci_regs.h>
+ #include <linux/pci-ecam.h>
+
+ #include "pcie-plda.h"
+
++static void plda_handle_msi(struct irq_desc *desc)
++{
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct device *dev = port->dev;
++ struct plda_msi *msi = &port->msi;
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ unsigned long status;
++ u32 bit;
++ int ret;
++
++ chained_irq_enter(chip, desc);
++
++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++ if (status & PM_MSI_INT_MSI_MASK) {
++ writel_relaxed(status & PM_MSI_INT_MSI_MASK,
++ bridge_base_addr + ISTATUS_LOCAL);
++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
++ for_each_set_bit(bit, &status, msi->num_vectors) {
++ ret = generic_handle_domain_irq(msi->dev_domain, bit);
++ if (ret)
++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
++ bit);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void plda_msi_bottom_irq_ack(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ u32 bitpos = data->hwirq;
++
++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
++}
++
++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ phys_addr_t addr = port->msi.vector_phy;
++
++ msg->address_lo = lower_32_bits(addr);
++ msg->address_hi = upper_32_bits(addr);
++ msg->data = data->hwirq;
++
++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
++ (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int plda_msi_set_affinity(struct irq_data *irq_data,
++ const struct cpumask *mask, bool force)
++{
++ return -EINVAL;
++}
++
++static struct irq_chip plda_msi_bottom_irq_chip = {
++ .name = "PLDA MSI",
++ .irq_ack = plda_msi_bottom_irq_ack,
++ .irq_compose_msi_msg = plda_compose_msi_msg,
++ .irq_set_affinity = plda_msi_set_affinity,
++};
++
++static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
++ unsigned int virq,
++ unsigned int nr_irqs,
++ void *args)
++{
++ struct plda_pcie_rp *port = domain->host_data;
++ struct plda_msi *msi = &port->msi;
++ unsigned long bit;
++
++ mutex_lock(&msi->lock);
++ bit = find_first_zero_bit(msi->used, msi->num_vectors);
++ if (bit >= msi->num_vectors) {
++ mutex_unlock(&msi->lock);
++ return -ENOSPC;
++ }
++
++ set_bit(bit, msi->used);
++
++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
++ domain->host_data, handle_edge_irq, NULL, NULL);
++
++ mutex_unlock(&msi->lock);
++
++ return 0;
++}
++
++static void plda_irq_msi_domain_free(struct irq_domain *domain,
++ unsigned int virq,
++ unsigned int nr_irqs)
++{
++ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
++ struct plda_msi *msi = &port->msi;
++
++ mutex_lock(&msi->lock);
++
++ if (test_bit(d->hwirq, msi->used))
++ __clear_bit(d->hwirq, msi->used);
++ else
++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
++
++ mutex_unlock(&msi->lock);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++ .alloc = plda_irq_msi_domain_alloc,
++ .free = plda_irq_msi_domain_free,
++};
++
++static struct irq_chip plda_msi_irq_chip = {
++ .name = "PLDA PCIe MSI",
++ .irq_ack = irq_chip_ack_parent,
++ .irq_mask = pci_msi_mask_irq,
++ .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info plda_msi_domain_info = {
++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++ MSI_FLAG_PCI_MSIX),
++ .chip = &plda_msi_irq_chip,
++};
++
++static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
++{
++ struct device *dev = port->dev;
++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++ struct plda_msi *msi = &port->msi;
++
++ mutex_init(&port->msi.lock);
++
++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
++ &msi_domain_ops, port);
++ if (!msi->dev_domain) {
++ dev_err(dev, "failed to create IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++ &plda_msi_domain_info,
++ msi->dev_domain);
++ if (!msi->msi_domain) {
++ dev_err(dev, "failed to create MSI domain\n");
++ irq_domain_remove(msi->dev_domain);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static void plda_handle_intx(struct irq_desc *desc)
++{
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct device *dev = port->dev;
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ unsigned long status;
++ u32 bit;
++ int ret;
++
++ chained_irq_enter(chip, desc);
++
++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++ if (status & PM_MSI_INT_INTX_MASK) {
++ status &= PM_MSI_INT_INTX_MASK;
++ status >>= PM_MSI_INT_INTX_SHIFT;
++ for_each_set_bit(bit, &status, PCI_NUM_INTX) {
++ ret = generic_handle_domain_irq(port->intx_domain, bit);
++ if (ret)
++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
++ bit);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void plda_ack_intx_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++
++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_intx_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ unsigned long flags;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++ u32 val;
++
++ raw_spin_lock_irqsave(&port->lock, flags);
++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++ val &= ~mask;
++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++ raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void plda_unmask_intx_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ void __iomem *bridge_base_addr = port->bridge_addr;
++ unsigned long flags;
++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++ u32 val;
++
++ raw_spin_lock_irqsave(&port->lock, flags);
++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++ val |= mask;
++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++ raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static struct irq_chip plda_intx_irq_chip = {
++ .name = "PLDA PCIe INTx",
++ .irq_ack = plda_ack_intx_irq,
++ .irq_mask = plda_mask_intx_irq,
++ .irq_unmask = plda_unmask_intx_irq,
++};
++
++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
++ irq_set_chip_data(irq, domain->host_data);
++
++ return 0;
++}
++
++static const struct irq_domain_ops intx_domain_ops = {
++ .map = plda_pcie_intx_map,
++};
++
++static u32 plda_get_events(struct plda_pcie_rp *port)
++{
++ u32 events, val, origin;
++
++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
++
++ /* MSI event and sys events */
++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
++
++ /* INTx events */
++ if (origin & PM_MSI_INT_INTX_MASK)
++ events |= BIT(PM_MSI_INT_INTX_SHIFT);
++
++ /* remains are same with register */
++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
++
++ return events;
++}
++
++static irqreturn_t plda_event_handler(int irq, void *dev_id)
++{
++ return IRQ_HANDLED;
++}
++
++static void plda_handle_event(struct irq_desc *desc)
++{
++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++ unsigned long events;
++ u32 bit;
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++
++ chained_irq_enter(chip, desc);
++
++ events = port->event_ops->get_events(port);
++
++ for_each_set_bit(bit, &events, port->num_events)
++ generic_handle_domain_irq(port->event_domain, bit);
++
++ chained_irq_exit(chip, desc);
++}
++
++static u32 plda_hwirq_to_mask(int hwirq)
++{
++ u32 mask;
++
++ /* hwirq 23 - 0 are the same with register */
++ if (hwirq < EVENT_PM_MSI_INT_INTX)
++ mask = BIT(hwirq);
++ else if (hwirq == EVENT_PM_MSI_INT_INTX)
++ mask = PM_MSI_INT_INTX_MASK;
++ else
++ mask = BIT(hwirq + PCI_NUM_INTX - 1);
++
++ return mask;
++}
++
++static void plda_ack_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++
++ writel_relaxed(plda_hwirq_to_mask(data->hwirq),
++ port->bridge_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ u32 mask, val;
++
++ mask = plda_hwirq_to_mask(data->hwirq);
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++ val &= ~mask;
++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++ raw_spin_unlock(&port->lock);
++}
++
++static void plda_unmask_event_irq(struct irq_data *data)
++{
++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++ u32 mask, val;
++
++ mask = plda_hwirq_to_mask(data->hwirq);
++
++ raw_spin_lock(&port->lock);
++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++ val |= mask;
++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++ raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip plda_event_irq_chip = {
++ .name = "PLDA PCIe EVENT",
++ .irq_ack = plda_ack_event_irq,
++ .irq_mask = plda_mask_event_irq,
++ .irq_unmask = plda_unmask_event_irq,
++};
++
++static const struct plda_event_ops plda_event_ops = {
++ .get_events = plda_get_events,
++};
++
++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ struct plda_pcie_rp *port = (void *)domain->host_data;
++
++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
++ irq_set_chip_data(irq, domain->host_data);
++
++ return 0;
++}
++
++static const struct irq_domain_ops plda_event_domain_ops = {
++ .map = plda_pcie_event_map,
++};
++
++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
++{
++ struct device *dev = port->dev;
++ struct device_node *node = dev->of_node;
++ struct device_node *pcie_intc_node;
++
++ /* Setup INTx */
++ pcie_intc_node = of_get_next_child(node, NULL);
++ if (!pcie_intc_node) {
++ dev_err(dev, "failed to find PCIe Intc node\n");
++ return -EINVAL;
++ }
++
++ port->event_domain = irq_domain_add_linear(pcie_intc_node,
++ port->num_events,
++ &plda_event_domain_ops,
++ port);
++ if (!port->event_domain) {
++ dev_err(dev, "failed to get event domain\n");
++ of_node_put(pcie_intc_node);
++ return -ENOMEM;
++ }
++
++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
++
++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++ &intx_domain_ops, port);
++ if (!port->intx_domain) {
++ dev_err(dev, "failed to get an INTx IRQ domain\n");
++ of_node_put(pcie_intc_node);
++ return -ENOMEM;
++ }
++
++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
++
++ of_node_put(pcie_intc_node);
++ raw_spin_lock_init(&port->lock);
++
++ return plda_allocate_msi_domains(port);
++}
++
++int plda_init_interrupts(struct platform_device *pdev,
++ struct plda_pcie_rp *port,
++ const struct plda_event *event)
++{
++ struct device *dev = &pdev->dev;
++ int irq;
++ int i, intx_irq, msi_irq, event_irq;
++ int ret;
++
++ if (!port->event_ops)
++ port->event_ops = &plda_event_ops;
++
++ if (!port->event_irq_chip)
++ port->event_irq_chip = &plda_event_irq_chip;
++
++ ret = plda_pcie_init_irq_domains(port);
++ if (ret) {
++ dev_err(dev, "failed creating IRQ domains\n");
++ return ret;
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return -ENODEV;
++
++ for (i = 0; i < port->num_events; i++) {
++ event_irq = irq_create_mapping(port->event_domain, i);
++ if (!event_irq) {
++ dev_err(dev, "failed to map hwirq %d\n", i);
++ return -ENXIO;
++ }
++
++ if (event->request_event_irq)
++ ret = event->request_event_irq(port, event_irq, i);
++ else
++ ret = devm_request_irq(dev, event_irq,
++ plda_event_handler,
++ 0, NULL, port);
++
++ if (ret) {
++ dev_err(dev, "failed to request IRQ %d\n", event_irq);
++ return ret;
++ }
++ }
++
++ intx_irq = irq_create_mapping(port->event_domain,
++ event->intx_event);
++ if (!intx_irq) {
++ dev_err(dev, "failed to map INTx interrupt\n");
++ return -ENXIO;
++ }
++
++ /* Plug the INTx chained handler */
++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
++
++ msi_irq = irq_create_mapping(port->event_domain,
++ event->msi_event);
++ if (!msi_irq)
++ return -ENXIO;
++
++ /* Plug the MSI chained handler */
++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
++
++ /* Plug the main event chained handler */
++ irq_set_chained_handler_and_data(irq, plda_handle_event, port);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(plda_init_interrupts);
++
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+ phys_addr_t axi_addr, phys_addr_t pci_addr,
+ size_t size)
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -169,6 +169,9 @@ struct plda_event {
+ int msi_event;
+ };
+
++int plda_init_interrupts(struct platform_device *pdev,
++ struct plda_pcie_rp *port,
++ const struct plda_event *event);
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+ phys_addr_t axi_addr, phys_addr_t pci_addr,
+ size_t size);
--- /dev/null
+From 142fc300fd7511a217783dcfa342031d8ad70188 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:07 +0800
+Subject: [PATCH 030/116] pci: plda: Add event bitmap field to struct
+ plda_pcie_rp
+
+For PLDA DMA interrupts are not all implemented. The non-implemented
+interrupts should be masked. So add a bitmap field to mask the non-
+implemented interrupts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 1 +
+ drivers/pci/controller/plda/pcie-plda-host.c | 6 ++++--
+ drivers/pci/controller/plda/pcie-plda.h | 1 +
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -636,6 +636,7 @@ static int mc_platform_init(struct pci_c
+
+ port->plda.event_ops = &mc_event_ops;
+ port->plda.event_irq_chip = &mc_event_irq_chip;
++ port->plda.events_bitmap = GENMASK(NUM_EVENTS - 1, 0);
+
+ /* Address translation is up; safe to enable interrupts */
+ ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -290,6 +290,7 @@ static void plda_handle_event(struct irq
+
+ events = port->event_ops->get_events(port);
+
++ events &= port->events_bitmap;
+ for_each_set_bit(bit, &events, port->num_events)
+ generic_handle_domain_irq(port->event_domain, bit);
+
+@@ -420,8 +421,9 @@ int plda_init_interrupts(struct platform
+ {
+ struct device *dev = &pdev->dev;
+ int irq;
+- int i, intx_irq, msi_irq, event_irq;
++ int intx_irq, msi_irq, event_irq;
+ int ret;
++ u32 i;
+
+ if (!port->event_ops)
+ port->event_ops = &plda_event_ops;
+@@ -439,7 +441,7 @@ int plda_init_interrupts(struct platform
+ if (irq < 0)
+ return -ENODEV;
+
+- for (i = 0; i < port->num_events; i++) {
++ for_each_set_bit(i, &port->events_bitmap, port->num_events) {
+ event_irq = irq_create_mapping(port->event_domain, i);
+ if (!event_irq) {
+ dev_err(dev, "failed to map hwirq %d\n", i);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -159,6 +159,7 @@ struct plda_pcie_rp {
+ const struct plda_event_ops *event_ops;
+ const struct irq_chip *event_irq_chip;
+ void __iomem *bridge_addr;
++ unsigned long events_bitmap;
+ int num_events;
+ };
+
--- /dev/null
+From 3b9991438094dc472dacb4555603bdc379653411 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:08 +0800
+Subject: [PATCH 031/116] PCI: plda: Add host init/deinit and map bus functions
+
+Add PLDA host plda_pcie_host_init()/plda_pcie_host_deinit() and map bus
+function. So vendor can use it to init PLDA PCIe host core.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+---
+ drivers/pci/controller/plda/pcie-plda-host.c | 131 +++++++++++++++++--
+ drivers/pci/controller/plda/pcie-plda.h | 22 ++++
+ 2 files changed, 139 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -3,6 +3,7 @@
+ * PLDA PCIe XpressRich host controller driver
+ *
+ * Copyright (C) 2023 Microchip Co. Ltd
++ * StarFive Co. Ltd
+ *
+ * Author: Daire McNamara <daire.mcnamara@microchip.com>
+ */
+@@ -15,6 +16,15 @@
+
+ #include "pcie-plda.h"
+
++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
++ int where)
++{
++ struct plda_pcie_rp *pcie = bus->sysdata;
++
++ return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_map_bus);
++
+ static void plda_handle_msi(struct irq_desc *desc)
+ {
+ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+@@ -420,9 +430,7 @@ int plda_init_interrupts(struct platform
+ const struct plda_event *event)
+ {
+ struct device *dev = &pdev->dev;
+- int irq;
+- int intx_irq, msi_irq, event_irq;
+- int ret;
++ int event_irq, ret;
+ u32 i;
+
+ if (!port->event_ops)
+@@ -437,8 +445,8 @@ int plda_init_interrupts(struct platform
+ return ret;
+ }
+
+- irq = platform_get_irq(pdev, 0);
+- if (irq < 0)
++ port->irq = platform_get_irq(pdev, 0);
++ if (port->irq < 0)
+ return -ENODEV;
+
+ for_each_set_bit(i, &port->events_bitmap, port->num_events) {
+@@ -461,26 +469,26 @@ int plda_init_interrupts(struct platform
+ }
+ }
+
+- intx_irq = irq_create_mapping(port->event_domain,
+- event->intx_event);
+- if (!intx_irq) {
++ port->intx_irq = irq_create_mapping(port->event_domain,
++ event->intx_event);
++ if (!port->intx_irq) {
+ dev_err(dev, "failed to map INTx interrupt\n");
+ return -ENXIO;
+ }
+
+ /* Plug the INTx chained handler */
+- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
++ irq_set_chained_handler_and_data(port->intx_irq, plda_handle_intx, port);
+
+- msi_irq = irq_create_mapping(port->event_domain,
+- event->msi_event);
+- if (!msi_irq)
++ port->msi_irq = irq_create_mapping(port->event_domain,
++ event->msi_event);
++ if (!port->msi_irq)
+ return -ENXIO;
+
+ /* Plug the MSI chained handler */
+- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
++ irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port);
+
+ /* Plug the main event chained handler */
+- irq_set_chained_handler_and_data(irq, plda_handle_event, port);
++ irq_set_chained_handler_and_data(port->irq, plda_handle_event, port);
+
+ return 0;
+ }
+@@ -546,3 +554,98 @@ int plda_pcie_setup_iomems(struct pci_ho
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
++
++static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie)
++{
++ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
++ irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
++ irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
++
++ irq_domain_remove(pcie->msi.msi_domain);
++ irq_domain_remove(pcie->msi.dev_domain);
++
++ irq_domain_remove(pcie->intx_domain);
++ irq_domain_remove(pcie->event_domain);
++}
++
++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
++ const struct plda_event *plda_event)
++{
++ struct device *dev = port->dev;
++ struct pci_host_bridge *bridge;
++ struct platform_device *pdev = to_platform_device(dev);
++ struct resource *cfg_res;
++ int ret;
++
++ pdev = to_platform_device(dev);
++
++ port->bridge_addr =
++ devm_platform_ioremap_resource_byname(pdev, "apb");
++
++ if (IS_ERR(port->bridge_addr))
++ return dev_err_probe(dev, PTR_ERR(port->bridge_addr),
++ "failed to map reg memory\n");
++
++ cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
++ if (!cfg_res)
++ return dev_err_probe(dev, -ENODEV,
++ "failed to get config memory\n");
++
++ port->config_base = devm_ioremap_resource(dev, cfg_res);
++ if (IS_ERR(port->config_base))
++ return dev_err_probe(dev, PTR_ERR(port->config_base),
++ "failed to map config memory\n");
++
++ bridge = devm_pci_alloc_host_bridge(dev, 0);
++ if (!bridge)
++ return dev_err_probe(dev, -ENOMEM,
++ "failed to alloc bridge\n");
++
++ if (port->host_ops && port->host_ops->host_init) {
++ ret = port->host_ops->host_init(port);
++ if (ret)
++ return ret;
++ }
++
++ port->bridge = bridge;
++ plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0,
++ resource_size(cfg_res));
++ plda_pcie_setup_iomems(bridge, port);
++ plda_set_default_msi(&port->msi);
++ ret = plda_init_interrupts(pdev, port, plda_event);
++ if (ret)
++ goto err_host;
++
++ /* Set default bus ops */
++ bridge->ops = ops;
++ bridge->sysdata = port;
++
++ ret = pci_host_probe(bridge);
++ if (ret < 0) {
++ dev_err_probe(dev, ret, "failed to probe pci host\n");
++ goto err_probe;
++ }
++
++ return ret;
++
++err_probe:
++ plda_pcie_irq_domain_deinit(port);
++err_host:
++ if (port->host_ops && port->host_ops->host_deinit)
++ port->host_ops->host_deinit(port);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(plda_pcie_host_init);
++
++void plda_pcie_host_deinit(struct plda_pcie_rp *port)
++{
++ pci_stop_root_bus(port->bridge->bus);
++ pci_remove_root_bus(port->bridge->bus);
++
++ plda_pcie_irq_domain_deinit(port);
++
++ if (port->host_ops && port->host_ops->host_deinit)
++ port->host_ops->host_deinit(port);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_host_deinit);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -141,6 +141,11 @@ struct plda_event_ops {
+ u32 (*get_events)(struct plda_pcie_rp *pcie);
+ };
+
++struct plda_pcie_host_ops {
++ int (*host_init)(struct plda_pcie_rp *pcie);
++ void (*host_deinit)(struct plda_pcie_rp *pcie);
++};
++
+ struct plda_msi {
+ struct mutex lock; /* Protect used bitmap */
+ struct irq_domain *msi_domain;
+@@ -152,14 +157,20 @@ struct plda_msi {
+
+ struct plda_pcie_rp {
+ struct device *dev;
++ struct pci_host_bridge *bridge;
+ struct irq_domain *intx_domain;
+ struct irq_domain *event_domain;
+ raw_spinlock_t lock;
+ struct plda_msi msi;
+ const struct plda_event_ops *event_ops;
+ const struct irq_chip *event_irq_chip;
++ const struct plda_pcie_host_ops *host_ops;
+ void __iomem *bridge_addr;
++ void __iomem *config_base;
+ unsigned long events_bitmap;
++ int irq;
++ int msi_irq;
++ int intx_irq;
+ int num_events;
+ };
+
+@@ -170,6 +181,8 @@ struct plda_event {
+ int msi_event;
+ };
+
++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
++ int where);
+ int plda_init_interrupts(struct platform_device *pdev,
+ struct plda_pcie_rp *port,
+ const struct plda_event *event);
+@@ -178,4 +191,13 @@ void plda_pcie_setup_window(void __iomem
+ size_t size);
+ int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+ struct plda_pcie_rp *port);
++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
++ const struct plda_event *plda_event);
++void plda_pcie_host_deinit(struct plda_pcie_rp *pcie);
++
++static inline void plda_set_default_msi(struct plda_msi *msi)
++{
++ msi->vector_phy = IMSI_ADDR;
++ msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS;
++}
+ #endif
--- /dev/null
+From bc3f8207d9f0af3cb96a7eae232074a644a175f6 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:09 +0800
+Subject: [PATCH 032/116] dt-bindings: PCI: Add StarFive JH7110 PCIe controller
+
+Add StarFive JH7110 SoC PCIe controller dt-bindings. JH7110 using PLDA
+XpressRICH PCIe host controller IP.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../bindings/pci/starfive,jh7110-pcie.yaml | 120 ++++++++++++++++++
+ 1 file changed, 120 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
+@@ -0,0 +1,120 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 PCIe host controller
++
++maintainers:
++ - Kevin Xie <kevin.xie@starfivetech.com>
++
++allOf:
++ - $ref: plda,xpressrich3-axi-common.yaml#
++
++properties:
++ compatible:
++ const: starfive,jh7110-pcie
++
++ clocks:
++ items:
++ - description: NOC bus clock
++ - description: Transport layer clock
++ - description: AXI MST0 clock
++ - description: APB clock
++
++ clock-names:
++ items:
++ - const: noc
++ - const: tl
++ - const: axi_mst0
++ - const: apb
++
++ resets:
++ items:
++ - description: AXI MST0 reset
++ - description: AXI SLAVE0 reset
++ - description: AXI SLAVE reset
++ - description: PCIE BRIDGE reset
++ - description: PCIE CORE reset
++ - description: PCIE APB reset
++
++ reset-names:
++ items:
++ - const: mst0
++ - const: slv0
++ - const: slv
++ - const: brg
++ - const: core
++ - const: apb
++
++ starfive,stg-syscon:
++ $ref: /schemas/types.yaml#/definitions/phandle-array
++ description:
++ The phandle to System Register Controller syscon node.
++
++ perst-gpios:
++ description: GPIO controlled connection to PERST# signal
++ maxItems: 1
++
++ phys:
++ description:
++ Specified PHY is attached to PCIe controller.
++ maxItems: 1
++
++required:
++ - clocks
++ - resets
++ - starfive,stg-syscon
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++ soc {
++ #address-cells = <2>;
++ #size-cells = <2>;
++
++ pcie@940000000 {
++ compatible = "starfive,jh7110-pcie";
++ reg = <0x9 0x40000000 0x0 0x10000000>,
++ <0x0 0x2b000000 0x0 0x1000000>;
++ reg-names = "cfg", "apb";
++ #address-cells = <3>;
++ #size-cells = <2>;
++ #interrupt-cells = <1>;
++ device_type = "pci";
++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>,
++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>;
++ starfive,stg-syscon = <&stg_syscon>;
++ bus-range = <0x0 0xff>;
++ interrupt-parent = <&plic>;
++ interrupts = <56>;
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>,
++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>,
++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>,
++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>;
++ msi-controller;
++ clocks = <&syscrg 86>,
++ <&stgcrg 10>,
++ <&stgcrg 8>,
++ <&stgcrg 9>;
++ clock-names = "noc", "tl", "axi_mst0", "apb";
++ resets = <&stgcrg 11>,
++ <&stgcrg 12>,
++ <&stgcrg 13>,
++ <&stgcrg 14>,
++ <&stgcrg 15>,
++ <&stgcrg 16>;
++ perst-gpios = <&gpios 26 GPIO_ACTIVE_LOW>;
++ phys = <&pciephy0>;
++
++ pcie_intc0: interrupt-controller {
++ #address-cells = <0>;
++ #interrupt-cells = <1>;
++ interrupt-controller;
++ };
++ };
++ };
--- /dev/null
+From abb20b7b8f5e3a7f36dbd6264e6d346275434154 Mon Sep 17 00:00:00 2001
+From: Kevin Xie <kevin.xie@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:10 +0800
+Subject: [PATCH 033/116] PCI: Add PCIE_RESET_CONFIG_DEVICE_WAIT_MS waiting
+ time value
+
+Add the PCIE_RESET_CONFIG_DEVICE_WAIT_MS macro to define the minimum
+waiting time between exit from a conventional reset and sending the
+first configuration request to the device.
+
+As described in PCI base specification r6.0, section 6.6.1 <Conventional
+Reset>, there are two different use cases of the value:
+
+ - "With a Downstream Port that does not support Link speeds greater
+ than 5.0 GT/s, software must wait a minimum of 100 ms following exit
+ from a Conventional Reset before sending a Configuration Request to
+ the device immediately below that Port."
+
+ - "With a Downstream Port that supports Link speeds greater than
+ 5.0 GT/s, software must wait a minimum of 100 ms after Link training
+ completes before sending a Configuration Request to the device
+ immediately below that Port."
+
+Signed-off-by: Kevin Xie <kevin.xie@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/pci.h | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -19,6 +19,22 @@
+ */
+ #define PCIE_PME_TO_L2_TIMEOUT_US 10000
+
++/*
++ * As described in PCI base specification r6.0, section 6.6.1 <Conventional
++ * Reset>, there are two different use cases of the value:
++ *
++ * - "With a Downstream Port that does not support Link speeds greater
++ * than 5.0 GT/s, software must wait a minimum of 100 ms following exit
++ * from a Conventional Reset before sending a Configuration Request to
++ * the device immediately below that Port."
++ *
++ * - "With a Downstream Port that supports Link speeds greater than
++ * 5.0 GT/s, software must wait a minimum of 100 ms after Link training
++ * completes before sending a Configuration Request to the device
++ * immediately below that Port."
++ */
++#define PCIE_RESET_CONFIG_DEVICE_WAIT_MS 100
++
+ extern const unsigned char pcie_link_speed[];
+ extern bool pci_early_dump;
+
--- /dev/null
+From 323aedef34315b758dc30ba23e2cabca259bb4b2 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:11 +0800
+Subject: [PATCH 034/116] PCI: starfive: Add JH7110 PCIe controller
+
+Add StarFive JH7110 SoC PCIe controller platform driver codes, JH7110
+with PLDA host PCIe core.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Co-developed-by: Kevin Xie <kevin.xie@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+---
+ drivers/pci/controller/plda/Kconfig | 12 +
+ drivers/pci/controller/plda/Makefile | 1 +
+ drivers/pci/controller/plda/pcie-plda.h | 71 ++-
+ drivers/pci/controller/plda/pcie-starfive.c | 473 ++++++++++++++++++++
+ 4 files changed, 556 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-starfive.c
+
+--- a/drivers/pci/controller/plda/Kconfig
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -15,4 +15,16 @@ config PCIE_MICROCHIP_HOST
+ Say Y here if you want kernel to support the Microchip AXI PCIe
+ Host Bridge driver.
+
++config PCIE_STARFIVE_HOST
++ tristate "StarFive PCIe host controller"
++ depends on PCI_MSI && OF
++ depends on ARCH_STARFIVE || COMPILE_TEST
++ select PCIE_PLDA_HOST
++ help
++ Say Y here if you want to support the StarFive PCIe controller in
++ host mode. StarFive PCIe controller uses PLDA PCIe core.
++
++ If you choose to build this driver as module it will be dynamically
++ linked and module will be called pcie-starfive.ko.
++
+ endmenu
+--- a/drivers/pci/controller/plda/Makefile
++++ b/drivers/pci/controller/plda/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o
+ obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
++obj-$(CONFIG_PCIE_STARFIVE_HOST) += pcie-starfive.o
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -10,10 +10,20 @@
+ #define PLDA_MAX_NUM_MSI_IRQS 32
+
+ /* PCIe Bridge Phy Regs */
++#define GEN_SETTINGS 0x80
++#define RP_ENABLE 1
++#define PCIE_PCI_IDS_DW1 0x9c
++#define IDS_CLASS_CODE_SHIFT 16
++#define REVISION_ID_MASK GENMASK(7, 0)
++#define CLASS_CODE_ID_MASK GENMASK(31, 8)
+ #define PCIE_PCI_IRQ_DW0 0xa8
+ #define MSIX_CAP_MASK BIT(31)
+ #define NUM_MSI_MSGS_MASK GENMASK(6, 4)
+ #define NUM_MSI_MSGS_SHIFT 4
++#define PCI_MISC 0xb4
++#define PHY_FUNCTION_DIS BIT(15)
++#define PCIE_WINROM 0xfc
++#define PREF_MEM_WIN_64_SUPPORT BIT(3)
+
+ #define IMASK_LOCAL 0x180
+ #define DMA_END_ENGINE_0_MASK 0x00000000u
+@@ -65,6 +75,8 @@
+ #define ISTATUS_HOST 0x18c
+ #define IMSI_ADDR 0x190
+ #define ISTATUS_MSI 0x194
++#define PMSG_SUPPORT_RX 0x3f0
++#define PMSG_LTR_SUPPORT BIT(2)
+
+ /* PCIe Master table init defines */
+ #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u
+@@ -86,6 +98,8 @@
+ #define PCIE_TX_RX_INTERFACE 0x00000000u
+ #define PCIE_CONFIG_INTERFACE 0x00000001u
+
++#define CONFIG_SPACE_ADDR_OFFSET 0x1000u
++
+ #define ATR_ENTRY_SIZE 32
+
+ enum plda_int_event {
+@@ -200,4 +214,59 @@ static inline void plda_set_default_msi(
+ msi->vector_phy = IMSI_ADDR;
+ msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS;
+ }
+-#endif
++
++static inline void plda_pcie_enable_root_port(struct plda_pcie_rp *plda)
++{
++ u32 value;
++
++ value = readl_relaxed(plda->bridge_addr + GEN_SETTINGS);
++ value |= RP_ENABLE;
++ writel_relaxed(value, plda->bridge_addr + GEN_SETTINGS);
++}
++
++static inline void plda_pcie_set_standard_class(struct plda_pcie_rp *plda)
++{
++ u32 value;
++
++ /* set class code and reserve revision id */
++ value = readl_relaxed(plda->bridge_addr + PCIE_PCI_IDS_DW1);
++ value &= REVISION_ID_MASK;
++ value |= (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT);
++ writel_relaxed(value, plda->bridge_addr + PCIE_PCI_IDS_DW1);
++}
++
++static inline void plda_pcie_set_pref_win_64bit(struct plda_pcie_rp *plda)
++{
++ u32 value;
++
++ value = readl_relaxed(plda->bridge_addr + PCIE_WINROM);
++ value |= PREF_MEM_WIN_64_SUPPORT;
++ writel_relaxed(value, plda->bridge_addr + PCIE_WINROM);
++}
++
++static inline void plda_pcie_disable_ltr(struct plda_pcie_rp *plda)
++{
++ u32 value;
++
++ value = readl_relaxed(plda->bridge_addr + PMSG_SUPPORT_RX);
++ value &= ~PMSG_LTR_SUPPORT;
++ writel_relaxed(value, plda->bridge_addr + PMSG_SUPPORT_RX);
++}
++
++static inline void plda_pcie_disable_func(struct plda_pcie_rp *plda)
++{
++ u32 value;
++
++ value = readl_relaxed(plda->bridge_addr + PCI_MISC);
++ value |= PHY_FUNCTION_DIS;
++ writel_relaxed(value, plda->bridge_addr + PCI_MISC);
++}
++
++static inline void plda_pcie_write_rc_bar(struct plda_pcie_rp *plda, u64 val)
++{
++ void __iomem *addr = plda->bridge_addr + CONFIG_SPACE_ADDR_OFFSET;
++
++ writel_relaxed(lower_32_bits(val), addr + PCI_BASE_ADDRESS_0);
++ writel_relaxed(upper_32_bits(val), addr + PCI_BASE_ADDRESS_1);
++}
++#endif /* _PCIE_PLDA_H */
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-starfive.c
+@@ -0,0 +1,473 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * PCIe host controller driver for StarFive JH7110 Soc.
++ *
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include "../../pci.h"
++
++#include "pcie-plda.h"
++
++#define PCIE_FUNC_NUM 4
++
++/* system control */
++#define STG_SYSCON_PCIE0_BASE 0x48
++#define STG_SYSCON_PCIE1_BASE 0x1f8
++
++#define STG_SYSCON_AR_OFFSET 0x78
++#define STG_SYSCON_AXI4_SLVL_AR_MASK GENMASK(22, 8)
++#define STG_SYSCON_AXI4_SLVL_PHY_AR(x) FIELD_PREP(GENMASK(20, 17), x)
++#define STG_SYSCON_AW_OFFSET 0x7c
++#define STG_SYSCON_AXI4_SLVL_AW_MASK GENMASK(14, 0)
++#define STG_SYSCON_AXI4_SLVL_PHY_AW(x) FIELD_PREP(GENMASK(12, 9), x)
++#define STG_SYSCON_CLKREQ BIT(22)
++#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18)
++#define STG_SYSCON_RP_NEP_OFFSET 0xe8
++#define STG_SYSCON_K_RP_NEP BIT(8)
++#define STG_SYSCON_LNKSTA_OFFSET 0x170
++#define DATA_LINK_ACTIVE BIT(5)
++
++/* Parameters for the waiting for link up routine */
++#define LINK_WAIT_MAX_RETRIES 10
++#define LINK_WAIT_USLEEP_MIN 90000
++#define LINK_WAIT_USLEEP_MAX 100000
++
++struct starfive_jh7110_pcie {
++ struct plda_pcie_rp plda;
++ struct reset_control *resets;
++ struct clk_bulk_data *clks;
++ struct regmap *reg_syscon;
++ struct gpio_desc *power_gpio;
++ struct gpio_desc *reset_gpio;
++ struct phy *phy;
++
++ unsigned int stg_pcie_base;
++ int num_clks;
++};
++
++/*
++ * The BAR0/1 of bridge should be hidden during enumeration to
++ * avoid the sizing and resource allocation by PCIe core.
++ */
++static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
++ int offset)
++{
++ if (pci_is_root_bus(bus) && !devfn &&
++ (offset == PCI_BASE_ADDRESS_0 || offset == PCI_BASE_ADDRESS_1))
++ return true;
++
++ return false;
++}
++
++static int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 value)
++{
++ if (starfive_pcie_hide_rc_bar(bus, devfn, where))
++ return PCIBIOS_SUCCESSFUL;
++
++ return pci_generic_config_write(bus, devfn, where, size, value);
++}
++
++static int starfive_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 *value)
++{
++ if (starfive_pcie_hide_rc_bar(bus, devfn, where)) {
++ *value = 0;
++ return PCIBIOS_SUCCESSFUL;
++ }
++
++ return pci_generic_config_read(bus, devfn, where, size, value);
++}
++
++static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie,
++ struct device *dev)
++{
++ int domain_nr;
++
++ pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks);
++ if (pcie->num_clks < 0)
++ return dev_err_probe(dev, pcie->num_clks,
++ "failed to get pcie clocks\n");
++
++ pcie->resets = devm_reset_control_array_get_exclusive(dev);
++ if (IS_ERR(pcie->resets))
++ return dev_err_probe(dev, PTR_ERR(pcie->resets),
++ "failed to get pcie resets");
++
++ pcie->reg_syscon =
++ syscon_regmap_lookup_by_phandle(dev->of_node,
++ "starfive,stg-syscon");
++
++ if (IS_ERR(pcie->reg_syscon))
++ return dev_err_probe(dev, PTR_ERR(pcie->reg_syscon),
++ "failed to parse starfive,stg-syscon\n");
++
++ pcie->phy = devm_phy_optional_get(dev, NULL);
++ if (IS_ERR(pcie->phy))
++ return dev_err_probe(dev, PTR_ERR(pcie->phy),
++ "failed to get pcie phy\n");
++
++ domain_nr = of_get_pci_domain_nr(dev->of_node);
++
++ if (domain_nr < 0 || domain_nr > 1)
++ return dev_err_probe(dev, -ENODEV,
++ "failed to get valid pcie domain\n");
++
++ if (domain_nr == 0)
++ pcie->stg_pcie_base = STG_SYSCON_PCIE0_BASE;
++ else
++ pcie->stg_pcie_base = STG_SYSCON_PCIE1_BASE;
++
++ pcie->reset_gpio = devm_gpiod_get_optional(dev, "perst",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(pcie->reset_gpio))
++ return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio),
++ "failed to get perst-gpio\n");
++
++ pcie->power_gpio = devm_gpiod_get_optional(dev, "enable",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(pcie->power_gpio))
++ return dev_err_probe(dev, PTR_ERR(pcie->power_gpio),
++ "failed to get power-gpio\n");
++
++ return 0;
++}
++
++static struct pci_ops starfive_pcie_ops = {
++ .map_bus = plda_pcie_map_bus,
++ .read = starfive_pcie_config_read,
++ .write = starfive_pcie_config_write,
++};
++
++static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie)
++{
++ struct device *dev = pcie->plda.dev;
++ int ret;
++
++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
++ if (ret)
++ return dev_err_probe(dev, ret, "failed to enable clocks\n");
++
++ ret = reset_control_deassert(pcie->resets);
++ if (ret) {
++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++ dev_err_probe(dev, ret, "failed to deassert resets\n");
++ }
++
++ return ret;
++}
++
++static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie)
++{
++ reset_control_assert(pcie->resets);
++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++}
++
++static bool starfive_pcie_link_up(struct plda_pcie_rp *plda)
++{
++ struct starfive_jh7110_pcie *pcie =
++ container_of(plda, struct starfive_jh7110_pcie, plda);
++ int ret;
++ u32 stg_reg_val;
++
++ ret = regmap_read(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_LNKSTA_OFFSET,
++ &stg_reg_val);
++ if (ret) {
++ dev_err(pcie->plda.dev, "failed to read link status\n");
++ return false;
++ }
++
++ return !!(stg_reg_val & DATA_LINK_ACTIVE);
++}
++
++static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie)
++{
++ int retries;
++
++ /* Check if the link is up or not */
++ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
++ if (starfive_pcie_link_up(&pcie->plda)) {
++ dev_info(pcie->plda.dev, "port link up\n");
++ return 0;
++ }
++ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
++ }
++
++ return -ETIMEDOUT;
++}
++
++static int starfive_pcie_enable_phy(struct device *dev,
++ struct starfive_jh7110_pcie *pcie)
++{
++ int ret;
++
++ if (!pcie->phy)
++ return 0;
++
++ ret = phy_init(pcie->phy);
++ if (ret)
++ return dev_err_probe(dev, ret,
++ "failed to initialize pcie phy\n");
++
++ ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE);
++ if (ret) {
++ dev_err_probe(dev, ret, "failed to set pcie mode\n");
++ goto err_phy_on;
++ }
++
++ ret = phy_power_on(pcie->phy);
++ if (ret) {
++ dev_err_probe(dev, ret, "failed to power on pcie phy\n");
++ goto err_phy_on;
++ }
++
++ return 0;
++
++err_phy_on:
++ phy_exit(pcie->phy);
++ return ret;
++}
++
++static void starfive_pcie_disable_phy(struct starfive_jh7110_pcie *pcie)
++{
++ phy_power_off(pcie->phy);
++ phy_exit(pcie->phy);
++}
++
++static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda)
++{
++ struct starfive_jh7110_pcie *pcie =
++ container_of(plda, struct starfive_jh7110_pcie, plda);
++
++ starfive_pcie_clk_rst_deinit(pcie);
++ if (pcie->power_gpio)
++ gpiod_set_value_cansleep(pcie->power_gpio, 0);
++ starfive_pcie_disable_phy(pcie);
++}
++
++static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
++{
++ struct starfive_jh7110_pcie *pcie =
++ container_of(plda, struct starfive_jh7110_pcie, plda);
++ struct device *dev = plda->dev;
++ int ret;
++ int i;
++
++ ret = starfive_pcie_enable_phy(dev, pcie);
++ if (ret)
++ return ret;
++
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET,
++ STG_SYSCON_K_RP_NEP, STG_SYSCON_K_RP_NEP);
++
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++ STG_SYSCON_CKREF_SRC_MASK,
++ FIELD_PREP(STG_SYSCON_CKREF_SRC_MASK, 2));
++
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++ STG_SYSCON_CLKREQ, STG_SYSCON_CLKREQ);
++
++ ret = starfive_pcie_clk_rst_init(pcie);
++ if (ret)
++ return ret;
++
++ if (pcie->power_gpio)
++ gpiod_set_value_cansleep(pcie->power_gpio, 1);
++
++ if (pcie->reset_gpio)
++ gpiod_set_value_cansleep(pcie->reset_gpio, 1);
++
++ /* Disable physical functions except #0 */
++ for (i = 1; i < PCIE_FUNC_NUM; i++) {
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET,
++ STG_SYSCON_AXI4_SLVL_AR_MASK,
++ STG_SYSCON_AXI4_SLVL_PHY_AR(i));
++
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++ STG_SYSCON_AXI4_SLVL_AW_MASK,
++ STG_SYSCON_AXI4_SLVL_PHY_AW(i));
++
++ plda_pcie_disable_func(plda);
++ }
++
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET,
++ STG_SYSCON_AXI4_SLVL_AR_MASK, 0);
++ regmap_update_bits(pcie->reg_syscon,
++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++ STG_SYSCON_AXI4_SLVL_AW_MASK, 0);
++
++ plda_pcie_enable_root_port(plda);
++ plda_pcie_write_rc_bar(plda, 0);
++
++ /* PCIe PCI Standard Configuration Identification Settings. */
++ plda_pcie_set_standard_class(plda);
++
++ /*
++ * The LTR message forwarding of PCIe Message Reception was set by core
++ * as default, but the forward id & addr are also need to be reset.
++ * If we do not disable LTR message forwarding here, or set a legal
++ * forwarding address, the kernel will get stuck after the driver probe.
++ * To workaround, disable the LTR message forwarding support on
++ * PCIe Message Reception.
++ */
++ plda_pcie_disable_ltr(plda);
++
++ /* Prefetchable memory window 64-bit addressing support */
++ plda_pcie_set_pref_win_64bit(plda);
++
++ /*
++ * Ensure that PERST has been asserted for at least 100 ms,
++ * the sleep value is T_PVPERL from PCIe CEM spec r2.0 (Table 2-4)
++ */
++ msleep(100);
++ if (pcie->reset_gpio)
++ gpiod_set_value_cansleep(pcie->reset_gpio, 0);
++
++ /*
++ * With a Downstream Port (<=5GT/s), software must wait a minimum
++ * of 100ms following exit from a conventional reset before
++ * sending a configuration request to the device.
++ */
++ msleep(PCIE_RESET_CONFIG_DEVICE_WAIT_MS);
++
++ if (starfive_pcie_host_wait_for_link(pcie))
++ dev_info(dev, "port link down\n");
++
++ return 0;
++}
++
++static const struct plda_pcie_host_ops sf_host_ops = {
++ .host_init = starfive_pcie_host_init,
++ .host_deinit = starfive_pcie_host_deinit,
++};
++
++static const struct plda_event stf_pcie_event = {
++ .intx_event = EVENT_PM_MSI_INT_INTX,
++ .msi_event = EVENT_PM_MSI_INT_MSI
++};
++
++static int starfive_pcie_probe(struct platform_device *pdev)
++{
++ struct starfive_jh7110_pcie *pcie;
++ struct device *dev = &pdev->dev;
++ struct plda_pcie_rp *plda;
++ int ret;
++
++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++ if (!pcie)
++ return -ENOMEM;
++
++ plda = &pcie->plda;
++ plda->dev = dev;
++
++ ret = starfive_pcie_parse_dt(pcie, dev);
++ if (ret)
++ return ret;
++
++ plda->host_ops = &sf_host_ops;
++ plda->num_events = PLDA_MAX_EVENT_NUM;
++ /* mask doorbell event */
++ plda->events_bitmap = GENMASK(PLDA_INT_EVENT_NUM - 1, 0)
++ & ~BIT(PLDA_AXI_DOORBELL)
++ & ~BIT(PLDA_PCIE_DOORBELL);
++ plda->events_bitmap <<= PLDA_NUM_DMA_EVENTS;
++ ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops,
++ &stf_pcie_event);
++ if (ret)
++ return ret;
++
++ pm_runtime_enable(&pdev->dev);
++ pm_runtime_get_sync(&pdev->dev);
++ platform_set_drvdata(pdev, pcie);
++
++ return 0;
++}
++
++static void starfive_pcie_remove(struct platform_device *pdev)
++{
++ struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev);
++
++ pm_runtime_put(&pdev->dev);
++ pm_runtime_disable(&pdev->dev);
++ plda_pcie_host_deinit(&pcie->plda);
++ platform_set_drvdata(pdev, NULL);
++}
++
++static int starfive_pcie_suspend_noirq(struct device *dev)
++{
++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev);
++
++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++ starfive_pcie_disable_phy(pcie);
++
++ return 0;
++}
++
++static int starfive_pcie_resume_noirq(struct device *dev)
++{
++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev);
++ int ret;
++
++ ret = starfive_pcie_enable_phy(dev, pcie);
++ if (ret)
++ return ret;
++
++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
++ if (ret) {
++ dev_err(dev, "failed to enable clocks\n");
++ starfive_pcie_disable_phy(pcie);
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct dev_pm_ops starfive_pcie_pm_ops = {
++ NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq,
++ starfive_pcie_resume_noirq)
++};
++
++static const struct of_device_id starfive_pcie_of_match[] = {
++ { .compatible = "starfive,jh7110-pcie", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, starfive_pcie_of_match);
++
++static struct platform_driver starfive_pcie_driver = {
++ .driver = {
++ .name = "pcie-starfive",
++ .of_match_table = of_match_ptr(starfive_pcie_of_match),
++ .pm = pm_sleep_ptr(&starfive_pcie_pm_ops),
++ },
++ .probe = starfive_pcie_probe,
++ .remove_new = starfive_pcie_remove,
++};
++module_platform_driver(starfive_pcie_driver);
++
++MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver");
++MODULE_LICENSE("GPL v2");
--- /dev/null
+From a306724fd4f32808d1e27efbd87019d56f60db20 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 14 Aug 2023 16:06:16 +0800
+Subject: [PATCH 035/116] ASoC: dt-bindings: Add StarFive JH7110 PWM-DAC
+ controller
+
+Add bindings for the PWM-DAC controller on the JH7110
+RISC-V SoC by StarFive Ltd.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Link: https://lore.kernel.org/r/20230814080618.10036-2-hal.feng@starfivetech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ .../sound/starfive,jh7110-pwmdac.yaml | 76 +++++++++++++++++++
+ 1 file changed, 76 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
+@@ -0,0 +1,76 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 PWM-DAC Controller
++
++description:
++ The PWM-DAC Controller uses PWM square wave generators plus RC filters to
++ form a DAC for audio play in StarFive JH7110 SoC. This audio play controller
++ supports 16 bit audio format, up to 48K sampling frequency, up to left and
++ right dual channels.
++
++maintainers:
++ - Hal Feng <hal.feng@starfivetech.com>
++
++allOf:
++ - $ref: dai-common.yaml#
++
++properties:
++ compatible:
++ const: starfive,jh7110-pwmdac
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ items:
++ - description: PWMDAC APB
++ - description: PWMDAC CORE
++
++ clock-names:
++ items:
++ - const: apb
++ - const: core
++
++ resets:
++ maxItems: 1
++ description: PWMDAC APB
++
++ dmas:
++ maxItems: 1
++ description: TX DMA Channel
++
++ dma-names:
++ const: tx
++
++ "#sound-dai-cells":
++ const: 0
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - clock-names
++ - resets
++ - dmas
++ - dma-names
++ - "#sound-dai-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ pwmdac@100b0000 {
++ compatible = "starfive,jh7110-pwmdac";
++ reg = <0x100b0000 0x1000>;
++ clocks = <&syscrg 157>,
++ <&syscrg 158>;
++ clock-names = "apb", "core";
++ resets = <&syscrg 96>;
++ dmas = <&dma 22>;
++ dma-names = "tx";
++ #sound-dai-cells = <0>;
++ };
--- /dev/null
+From a79d2ec524012e35e32a2c4ae2401d0aa763697d Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 14 Aug 2023 16:06:17 +0800
+Subject: [PATCH 036/116] ASoC: starfive: Add JH7110 PWM-DAC driver
+
+Add PWM-DAC driver support for the StarFive JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Link: https://lore.kernel.org/r/20230814080618.10036-3-hal.feng@starfivetech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/Kconfig | 9 +
+ sound/soc/starfive/Makefile | 1 +
+ sound/soc/starfive/jh7110_pwmdac.c | 529 +++++++++++++++++++++++++++++
+ 3 files changed, 539 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_pwmdac.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -7,6 +7,15 @@ config SND_SOC_STARFIVE
+ the Starfive SoCs' Audio interfaces. You will also need to
+ select the audio interfaces to support below.
+
++config SND_SOC_JH7110_PWMDAC
++ tristate "JH7110 PWM-DAC device driver"
++ depends on HAVE_CLK && SND_SOC_STARFIVE
++ select SND_SOC_GENERIC_DMAENGINE_PCM
++ select SND_SOC_SPDIF
++ help
++ Say Y or M if you want to add support for StarFive JH7110
++ PWM-DAC driver.
++
+ config SND_SOC_JH7110_TDM
+ tristate "JH7110 TDM device driver"
+ depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,2 +1,3 @@
+ # StarFive Platform Support
++obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
+ obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -0,0 +1,529 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ * Authors: Jenny Zhang
++ * Curry Zhang
++ * Xingyu Wu <xingyu.wu@starfivetech.com>
++ * Hal Feng <hal.feng@starfivetech.com>
++ */
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#define JH7110_PWMDAC_WDATA 0x00
++#define JH7110_PWMDAC_CTRL 0x04
++ #define JH7110_PWMDAC_ENABLE BIT(0)
++ #define JH7110_PWMDAC_SHIFT BIT(1)
++ #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT 2
++ #define JH7110_PWMDAC_DUTY_CYCLE_MASK GENMASK(3, 2)
++ #define JH7110_PWMDAC_CNT_N_SHIFT 4
++ #define JH7110_PWMDAC_CNT_N_MASK GENMASK(12, 4)
++ #define JH7110_PWMDAC_DATA_CHANGE BIT(13)
++ #define JH7110_PWMDAC_DATA_MODE BIT(14)
++ #define JH7110_PWMDAC_DATA_SHIFT_SHIFT 15
++ #define JH7110_PWMDAC_DATA_SHIFT_MASK GENMASK(17, 15)
++
++enum JH7110_PWMDAC_SHIFT_VAL {
++ PWMDAC_SHIFT_8 = 0,
++ PWMDAC_SHIFT_10,
++};
++
++enum JH7110_PWMDAC_DUTY_CYCLE_VAL {
++ PWMDAC_CYCLE_LEFT = 0,
++ PWMDAC_CYCLE_RIGHT,
++ PWMDAC_CYCLE_CENTER,
++};
++
++enum JH7110_PWMDAC_CNT_N_VAL {
++ PWMDAC_SAMPLE_CNT_1 = 1,
++ PWMDAC_SAMPLE_CNT_2,
++ PWMDAC_SAMPLE_CNT_3,
++ PWMDAC_SAMPLE_CNT_512 = 512, /* max */
++};
++
++enum JH7110_PWMDAC_DATA_CHANGE_VAL {
++ NO_CHANGE = 0,
++ CHANGE,
++};
++
++enum JH7110_PWMDAC_DATA_MODE_VAL {
++ UNSIGNED_DATA = 0,
++ INVERTER_DATA_MSB,
++};
++
++enum JH7110_PWMDAC_DATA_SHIFT_VAL {
++ PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_1,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_2,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_3,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_4,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_5,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_6,
++ PWMDAC_DATA_LEFT_SHIFT_BIT_7,
++};
++
++struct jh7110_pwmdac_cfg {
++ enum JH7110_PWMDAC_SHIFT_VAL shift;
++ enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle;
++ u16 cnt_n;
++ enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change;
++ enum JH7110_PWMDAC_DATA_MODE_VAL data_mode;
++ enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift;
++};
++
++struct jh7110_pwmdac_dev {
++ void __iomem *base;
++ resource_size_t mapbase;
++ struct jh7110_pwmdac_cfg cfg;
++
++ struct clk_bulk_data clks[2];
++ struct reset_control *rst_apb;
++ struct device *dev;
++ struct snd_dmaengine_dai_dma_data play_dma_data;
++ u32 saved_ctrl;
++};
++
++static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val)
++{
++ writel(val, io_base + reg);
++}
++
++static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg)
++{
++ return readl(io_base + reg);
++}
++
++static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ if (enable)
++ value |= JH7110_PWMDAC_ENABLE;
++ else
++ value &= ~JH7110_PWMDAC_ENABLE;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ if (dev->cfg.shift == PWMDAC_SHIFT_8)
++ value &= ~JH7110_PWMDAC_SHIFT;
++ else if (dev->cfg.shift == PWMDAC_SHIFT_10)
++ value |= JH7110_PWMDAC_SHIFT;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK;
++ value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ value &= ~JH7110_PWMDAC_CNT_N_MASK;
++ value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ if (dev->cfg.data_change == NO_CHANGE)
++ value &= ~JH7110_PWMDAC_DATA_CHANGE;
++ else if (dev->cfg.data_change == CHANGE)
++ value |= JH7110_PWMDAC_DATA_CHANGE;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ if (dev->cfg.data_mode == UNSIGNED_DATA)
++ value &= ~JH7110_PWMDAC_DATA_MODE;
++ else if (dev->cfg.data_mode == INVERTER_DATA_MSB)
++ value |= JH7110_PWMDAC_DATA_MODE;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev)
++{
++ u32 value;
++
++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++ value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK;
++ value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT;
++
++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev)
++{
++ jh7110_pwmdac_set_shift(dev);
++ jh7110_pwmdac_set_duty_cycle(dev);
++ jh7110_pwmdac_set_cnt_n(dev);
++ jh7110_pwmdac_set_enable(dev, true);
++
++ jh7110_pwmdac_set_data_change(dev);
++ jh7110_pwmdac_set_data_mode(dev);
++ jh7110_pwmdac_set_data_shift(dev);
++}
++
++static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev)
++{
++ jh7110_pwmdac_set_enable(dev, false);
++}
++
++static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++ struct snd_soc_dai_link *dai_link = rtd->dai_link;
++
++ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
++
++ return 0;
++}
++
++static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
++ unsigned long core_clk_rate;
++ int ret;
++
++ switch (params_rate(params)) {
++ case 8000:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;
++ core_clk_rate = 6144000;
++ break;
++ case 11025:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2;
++ core_clk_rate = 5644800;
++ break;
++ case 16000:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;
++ core_clk_rate = 12288000;
++ break;
++ case 22050:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++ core_clk_rate = 5644800;
++ break;
++ case 32000:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++ core_clk_rate = 8192000;
++ break;
++ case 44100:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++ core_clk_rate = 11289600;
++ break;
++ case 48000:
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++ core_clk_rate = 12288000;
++ break;
++ default:
++ dev_err(dai->dev, "%d rate not supported\n",
++ params_rate(params));
++ return -EINVAL;
++ }
++
++ switch (params_channels(params)) {
++ case 1:
++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
++ break;
++ case 2:
++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ break;
++ default:
++ dev_err(dai->dev, "%d channels not supported\n",
++ params_channels(params));
++ return -EINVAL;
++ }
++
++ /*
++ * The clock rate always rounds down when using clk_set_rate()
++ * so increase the rate a bit
++ */
++ core_clk_rate += 64;
++ jh7110_pwmdac_set(dev);
++
++ ret = clk_set_rate(dev->clks[1].clk, core_clk_rate);
++ if (ret)
++ return dev_err_probe(dai->dev, ret,
++ "failed to set rate %lu for core clock\n",
++ core_clk_rate);
++
++ return 0;
++}
++
++static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ jh7110_pwmdac_set(dev);
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ jh7110_pwmdac_stop(dev);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable)
++{
++ int ret;
++
++ if (enable) {
++ ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks);
++ if (ret)
++ return dev_err_probe(dev->dev, ret,
++ "failed to enable pwmdac clocks\n");
++
++ ret = reset_control_deassert(dev->rst_apb);
++ if (ret) {
++ dev_err(dev->dev, "failed to deassert pwmdac apb reset\n");
++ goto err_rst_apb;
++ }
++ } else {
++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);
++ }
++
++ return 0;
++
++err_rst_apb:
++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);
++
++ return ret;
++}
++
++static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai)
++{
++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
++
++ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
++ snd_soc_dai_set_drvdata(dai, dev);
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = {
++ .startup = jh7110_pwmdac_startup,
++ .hw_params = jh7110_pwmdac_hw_params,
++ .trigger = jh7110_pwmdac_trigger,
++};
++
++static const struct snd_soc_component_driver jh7110_pwmdac_component = {
++ .name = "jh7110-pwmdac",
++};
++
++static struct snd_soc_dai_driver jh7110_pwmdac_dai = {
++ .name = "jh7110-pwmdac",
++ .id = 0,
++ .probe = jh7110_pwmdac_dai_probe,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = SNDRV_PCM_RATE_8000_48000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ },
++ .ops = &jh7110_pwmdac_dai_ops,
++};
++
++static int jh7110_pwmdac_runtime_suspend(struct device *dev)
++{
++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++ return jh7110_pwmdac_crg_enable(pwmdac, false);
++}
++
++static int jh7110_pwmdac_runtime_resume(struct device *dev)
++{
++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++ return jh7110_pwmdac_crg_enable(pwmdac, true);
++}
++
++static int jh7110_pwmdac_system_suspend(struct device *dev)
++{
++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++ /* save the CTRL register value */
++ pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base,
++ JH7110_PWMDAC_CTRL);
++ return pm_runtime_force_suspend(dev);
++}
++
++static int jh7110_pwmdac_system_resume(struct device *dev)
++{
++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++ int ret;
++
++ ret = pm_runtime_force_resume(dev);
++ if (ret)
++ return ret;
++
++ /* restore the CTRL register value */
++ jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL,
++ pwmdac->saved_ctrl);
++ return 0;
++}
++
++static const struct dev_pm_ops jh7110_pwmdac_pm_ops = {
++ RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend,
++ jh7110_pwmdac_runtime_resume, NULL)
++ SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend,
++ jh7110_pwmdac_system_resume)
++};
++
++static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev)
++{
++ dev->cfg.shift = PWMDAC_SHIFT_8;
++ dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER;
++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++ dev->cfg.data_change = NO_CHANGE;
++ dev->cfg.data_mode = INVERTER_DATA_MSB;
++ dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0;
++
++ dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA;
++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ dev->play_dma_data.fifo_size = 1;
++ dev->play_dma_data.maxburst = 16;
++}
++
++static int jh7110_pwmdac_probe(struct platform_device *pdev)
++{
++ struct jh7110_pwmdac_dev *dev;
++ struct resource *res;
++ int ret;
++
++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++
++ dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
++ if (IS_ERR(dev->base))
++ return PTR_ERR(dev->base);
++
++ dev->mapbase = res->start;
++
++ dev->clks[0].id = "apb";
++ dev->clks[1].id = "core";
++
++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks);
++ if (ret)
++ return dev_err_probe(&pdev->dev, ret,
++ "failed to get pwmdac clocks\n");
++
++ dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++ if (IS_ERR(dev->rst_apb))
++ return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb),
++ "failed to get pwmdac apb reset\n");
++
++ jh7110_pwmdac_init_params(dev);
++
++ dev->dev = &pdev->dev;
++ dev_set_drvdata(&pdev->dev, dev);
++ ret = devm_snd_soc_register_component(&pdev->dev,
++ &jh7110_pwmdac_component,
++ &jh7110_pwmdac_dai, 1);
++ if (ret)
++ return dev_err_probe(&pdev->dev, ret, "failed to register dai\n");
++
++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
++ if (ret)
++ return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n");
++
++ pm_runtime_enable(dev->dev);
++ if (!pm_runtime_enabled(&pdev->dev)) {
++ ret = jh7110_pwmdac_runtime_resume(&pdev->dev);
++ if (ret)
++ goto err_pm_disable;
++ }
++
++ return 0;
++
++err_pm_disable:
++ pm_runtime_disable(&pdev->dev);
++
++ return ret;
++}
++
++static int jh7110_pwmdac_remove(struct platform_device *pdev)
++{
++ pm_runtime_disable(&pdev->dev);
++ return 0;
++}
++
++static const struct of_device_id jh7110_pwmdac_of_match[] = {
++ { .compatible = "starfive,jh7110-pwmdac" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match);
++
++static struct platform_driver jh7110_pwmdac_driver = {
++ .driver = {
++ .name = "jh7110-pwmdac",
++ .of_match_table = jh7110_pwmdac_of_match,
++ .pm = pm_ptr(&jh7110_pwmdac_pm_ops),
++ },
++ .probe = jh7110_pwmdac_probe,
++ .remove = jh7110_pwmdac_remove,
++};
++module_platform_driver(jh7110_pwmdac_driver);
++
++MODULE_AUTHOR("Jenny Zhang");
++MODULE_AUTHOR("Curry Zhang");
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From c6b693f990e1f89ab5af0a139da31401b8cda74f Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@kernel.org>
+Date: Mon, 11 Sep 2023 23:48:39 +0100
+Subject: [PATCH 037/116] ASoC: Update jh7110 PWM DAC for ops move
+
+For some reason the JH7110 PWM DAC driver made it through build testing
+in spite of not being updated for the move of probe() to the ops struct.
+Make the required update.
+
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/jh7110_pwmdac.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/sound/soc/starfive/jh7110_pwmdac.c
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -357,6 +357,7 @@ static int jh7110_pwmdac_dai_probe(struc
+ }
+
+ static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = {
++ .probe = jh7110_pwmdac_dai_probe,
+ .startup = jh7110_pwmdac_startup,
+ .hw_params = jh7110_pwmdac_hw_params,
+ .trigger = jh7110_pwmdac_trigger,
+@@ -369,7 +370,6 @@ static const struct snd_soc_component_dr
+ static struct snd_soc_dai_driver jh7110_pwmdac_dai = {
+ .name = "jh7110-pwmdac",
+ .id = 0,
+- .probe = jh7110_pwmdac_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
--- /dev/null
+From 312c3c407c363f0ec7417d2d389cbe936c503729 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Sat, 14 Oct 2023 00:19:49 +0200
+Subject: [PATCH 038/116] ASoC: starfive/jh7110-pwmdac: Convert to platform
+ remove callback returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart
+from emitting a warning) and this typically results in resource leaks.
+
+To improve here there is a quest to make the remove callback return
+void. In the first step of this quest all drivers are converted to
+.remove_new(), which already returns void. Eventually after all drivers
+are converted, .remove_new() will be renamed to .remove().
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Link: https://lore.kernel.org/r/20231013221945.1489203-12-u.kleine-koenig@pengutronix.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/jh7110_pwmdac.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/starfive/jh7110_pwmdac.c
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -498,10 +498,9 @@ err_pm_disable:
+ return ret;
+ }
+
+-static int jh7110_pwmdac_remove(struct platform_device *pdev)
++static void jh7110_pwmdac_remove(struct platform_device *pdev)
+ {
+ pm_runtime_disable(&pdev->dev);
+- return 0;
+ }
+
+ static const struct of_device_id jh7110_pwmdac_of_match[] = {
+@@ -517,7 +516,7 @@ static struct platform_driver jh7110_pwm
+ .pm = pm_ptr(&jh7110_pwmdac_pm_ops),
+ },
+ .probe = jh7110_pwmdac_probe,
+- .remove = jh7110_pwmdac_remove,
++ .remove_new = jh7110_pwmdac_remove,
+ };
+ module_platform_driver(jh7110_pwmdac_driver);
+
--- /dev/null
+From 8d84bac6d7471ba2e29b33d19a2ef887822e9cce Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:25 +0100
+Subject: [PATCH 039/116] dt-bindings: power: Add power-domain header for
+ JH7110
+
+Add power-domain header for JH7110 SoC, it can use to operate dphy
+power.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-grumbly-rewrite-34c85539f2ed@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ include/dt-bindings/power/starfive,jh7110-pmu.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/include/dt-bindings/power/starfive,jh7110-pmu.h
++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+ /*
+- * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
+ * Author: Walker Chen <walker.chen@starfivetech.com>
+ */
+ #ifndef __DT_BINDINGS_POWER_JH7110_POWER_H__
+@@ -14,4 +14,7 @@
+ #define JH7110_PD_ISP 5
+ #define JH7110_PD_VENC 6
+
++#define JH7110_PD_DPHY_TX 0
++#define JH7110_PD_DPHY_RX 1
++
+ #endif
--- /dev/null
+From 0ac8e8b0e65d242f455401df0cc6c6d4772216e6 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:26 +0100
+Subject: [PATCH 040/116] pmdomain: starfive: Replace SOC_STARFIVE with
+ ARCH_STARFIVE
+
+Using ARCH_FOO symbol is preferred than SOC_FOO.
+
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-legibly-treachery-567cffcb5604@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/soc/starfive/Kconfig | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/soc/starfive/Kconfig
++++ b/drivers/soc/starfive/Kconfig
+@@ -3,8 +3,8 @@
+ config JH71XX_PMU
+ bool "Support PMU for StarFive JH71XX Soc"
+ depends on PM
+- depends on SOC_STARFIVE || COMPILE_TEST
+- default SOC_STARFIVE
++ depends on ARCH_STARFIVE || COMPILE_TEST
++ default ARCH_STARFIVE
+ select PM_GENERIC_DOMAINS
+ help
+ Say 'y' here to enable support power domain support.
--- /dev/null
+From a1ba60e35ca7f1b85243054556ecde2e259619e1 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:27 +0100
+Subject: [PATCH 041/116] pmdomain: starfive: Extract JH7110 pmu private
+ operations
+
+Move JH7110 private operation into private data of compatible. Convenient
+to add AON PMU which would not have interrupts property.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-slideshow-luckiness-38ff17de84c6@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 89 ++++++++++++++++++--------
+ 1 file changed, 62 insertions(+), 27 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -51,9 +51,17 @@ struct jh71xx_domain_info {
+ u8 bit;
+ };
+
++struct jh71xx_pmu;
++struct jh71xx_pmu_dev;
++
+ struct jh71xx_pmu_match_data {
+ const struct jh71xx_domain_info *domain_info;
+ int num_domains;
++ unsigned int pmu_status;
++ int (*pmu_parse_irq)(struct platform_device *pdev,
++ struct jh71xx_pmu *pmu);
++ int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd,
++ u32 mask, bool on);
+ };
+
+ struct jh71xx_pmu {
+@@ -79,12 +87,12 @@ static int jh71xx_pmu_get_state(struct j
+ if (!mask)
+ return -EINVAL;
+
+- *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask;
++ *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask;
+
+ return 0;
+ }
+
+-static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
+ {
+ struct jh71xx_pmu *pmu = pmd->pmu;
+ unsigned long flags;
+@@ -92,22 +100,8 @@ static int jh71xx_pmu_set_state(struct j
+ u32 mode;
+ u32 encourage_lo;
+ u32 encourage_hi;
+- bool is_on;
+ int ret;
+
+- ret = jh71xx_pmu_get_state(pmd, mask, &is_on);
+- if (ret) {
+- dev_dbg(pmu->dev, "unable to get current state for %s\n",
+- pmd->genpd.name);
+- return ret;
+- }
+-
+- if (is_on == on) {
+- dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n",
+- pmd->genpd.name, on ? "en" : "dis");
+- return 0;
+- }
+-
+ spin_lock_irqsave(&pmu->lock, flags);
+
+ /*
+@@ -166,6 +160,29 @@ static int jh71xx_pmu_set_state(struct j
+ return 0;
+ }
+
++static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++{
++ struct jh71xx_pmu *pmu = pmd->pmu;
++ const struct jh71xx_pmu_match_data *match_data = pmu->match_data;
++ bool is_on;
++ int ret;
++
++ ret = jh71xx_pmu_get_state(pmd, mask, &is_on);
++ if (ret) {
++ dev_dbg(pmu->dev, "unable to get current state for %s\n",
++ pmd->genpd.name);
++ return ret;
++ }
++
++ if (is_on == on) {
++ dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n",
++ pmd->genpd.name, on ? "en" : "dis");
++ return 0;
++ }
++
++ return match_data->pmu_set_state(pmd, mask, on);
++}
++
+ static int jh71xx_pmu_on(struct generic_pm_domain *genpd)
+ {
+ struct jh71xx_pmu_dev *pmd = container_of(genpd,
+@@ -226,6 +243,25 @@ static irqreturn_t jh71xx_pmu_interrupt(
+ return IRQ_HANDLED;
+ }
+
++static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu)
++{
++ struct device *dev = &pdev->dev;
++ int ret;
++
++ pmu->irq = platform_get_irq(pdev, 0);
++ if (pmu->irq < 0)
++ return pmu->irq;
++
++ ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt,
++ 0, pdev->name, pmu);
++ if (ret)
++ dev_err(dev, "failed to request irq\n");
++
++ jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true);
++
++ return 0;
++}
++
+ static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index)
+ {
+ struct jh71xx_pmu_dev *pmd;
+@@ -275,19 +311,18 @@ static int jh71xx_pmu_probe(struct platf
+ if (IS_ERR(pmu->base))
+ return PTR_ERR(pmu->base);
+
+- pmu->irq = platform_get_irq(pdev, 0);
+- if (pmu->irq < 0)
+- return pmu->irq;
+-
+- ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt,
+- 0, pdev->name, pmu);
+- if (ret)
+- dev_err(dev, "failed to request irq\n");
++ spin_lock_init(&pmu->lock);
+
+ match_data = of_device_get_match_data(dev);
+ if (!match_data)
+ return -EINVAL;
+
++ ret = match_data->pmu_parse_irq(pdev, pmu);
++ if (ret) {
++ dev_err(dev, "failed to parse irq\n");
++ return ret;
++ }
++
+ pmu->genpd = devm_kcalloc(dev, match_data->num_domains,
+ sizeof(struct generic_pm_domain *),
+ GFP_KERNEL);
+@@ -307,9 +342,6 @@ static int jh71xx_pmu_probe(struct platf
+ }
+ }
+
+- spin_lock_init(&pmu->lock);
+- jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true);
+-
+ ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data);
+ if (ret) {
+ dev_err(dev, "failed to register genpd driver: %d\n", ret);
+@@ -357,6 +389,9 @@ static const struct jh71xx_domain_info j
+ static const struct jh71xx_pmu_match_data jh7110_pmu = {
+ .num_domains = ARRAY_SIZE(jh7110_power_domains),
+ .domain_info = jh7110_power_domains,
++ .pmu_status = JH71XX_PMU_CURR_POWER_MODE,
++ .pmu_parse_irq = jh7110_pmu_parse_irq,
++ .pmu_set_state = jh7110_pmu_set_state,
+ };
+
+ static const struct of_device_id jh71xx_pmu_of_match[] = {
--- /dev/null
+From 1bf849b606d0b4cae643f96685d3d3981643683d Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:28 +0100
+Subject: [PATCH 042/116] pmdomain: starfive: Add JH7110 AON PMU support
+
+Add AON PMU for StarFive JH7110 SoC. It can be used to turn on/off the
+dphy rx/tx power switch.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-dude-imprecise-fc32622bc947@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 57 +++++++++++++++++++++++---
+ 1 file changed, 52 insertions(+), 5 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -2,7 +2,7 @@
+ /*
+ * StarFive JH71XX PMU (Power Management Unit) Controller Driver
+ *
+- * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
+ */
+
+ #include <linux/interrupt.h>
+@@ -24,6 +24,9 @@
+ #define JH71XX_PMU_EVENT_STATUS 0x88
+ #define JH71XX_PMU_INT_STATUS 0x8C
+
++/* aon pmu register offset */
++#define JH71XX_AON_PMU_SWITCH 0x00
++
+ /* sw encourage cfg */
+ #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05
+ #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50
+@@ -160,6 +163,26 @@ static int jh7110_pmu_set_state(struct j
+ return 0;
+ }
+
++static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++{
++ struct jh71xx_pmu *pmu = pmd->pmu;
++ unsigned long flags;
++ u32 val;
++
++ spin_lock_irqsave(&pmu->lock, flags);
++ val = readl(pmu->base + JH71XX_AON_PMU_SWITCH);
++
++ if (on)
++ val |= mask;
++ else
++ val &= ~mask;
++
++ writel(val, pmu->base + JH71XX_AON_PMU_SWITCH);
++ spin_unlock_irqrestore(&pmu->lock, flags);
++
++ return 0;
++}
++
+ static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
+ {
+ struct jh71xx_pmu *pmu = pmd->pmu;
+@@ -317,10 +340,12 @@ static int jh71xx_pmu_probe(struct platf
+ if (!match_data)
+ return -EINVAL;
+
+- ret = match_data->pmu_parse_irq(pdev, pmu);
+- if (ret) {
+- dev_err(dev, "failed to parse irq\n");
+- return ret;
++ if (match_data->pmu_parse_irq) {
++ ret = match_data->pmu_parse_irq(pdev, pmu);
++ if (ret) {
++ dev_err(dev, "failed to parse irq\n");
++ return ret;
++ }
+ }
+
+ pmu->genpd = devm_kcalloc(dev, match_data->num_domains,
+@@ -394,11 +419,32 @@ static const struct jh71xx_pmu_match_dat
+ .pmu_set_state = jh7110_pmu_set_state,
+ };
+
++static const struct jh71xx_domain_info jh7110_aon_power_domains[] = {
++ [JH7110_PD_DPHY_TX] = {
++ .name = "DPHY-TX",
++ .bit = 30,
++ },
++ [JH7110_PD_DPHY_RX] = {
++ .name = "DPHY-RX",
++ .bit = 31,
++ },
++};
++
++static const struct jh71xx_pmu_match_data jh7110_aon_pmu = {
++ .num_domains = ARRAY_SIZE(jh7110_aon_power_domains),
++ .domain_info = jh7110_aon_power_domains,
++ .pmu_status = JH71XX_AON_PMU_SWITCH,
++ .pmu_set_state = jh7110_aon_pmu_set_state,
++};
++
+ static const struct of_device_id jh71xx_pmu_of_match[] = {
+ {
+ .compatible = "starfive,jh7110-pmu",
+ .data = (void *)&jh7110_pmu,
+ }, {
++ .compatible = "starfive,jh7110-aon-syscon",
++ .data = (void *)&jh7110_aon_pmu,
++ }, {
+ /* sentinel */
+ }
+ };
+@@ -414,5 +460,6 @@ static struct platform_driver jh71xx_pmu
+ builtin_platform_driver(jh71xx_pmu_driver);
+
+ MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
++MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
+ MODULE_DESCRIPTION("StarFive JH71XX PMU Driver");
+ MODULE_LICENSE("GPL");
--- /dev/null
+From dff6605dcd1fc1e2af1437e59187a6f71ce389cd Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+Date: Mon, 11 Sep 2023 17:22:15 +0200
+Subject: [PATCH 043/116] pmdomain: Prepare to move Kconfig files into the
+ pmdomain subsystem
+
+Rather than having the various Kconfig files for the genpd providers
+sprinkled across subsystems, let's prepare to move them into the pmdomain
+subsystem along with the implementations.
+
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/Kconfig | 2 ++
+ drivers/pmdomain/Kconfig | 4 ++++
+ 2 files changed, 6 insertions(+)
+ create mode 100644 drivers/pmdomain/Kconfig
+
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -175,6 +175,8 @@ source "drivers/soundwire/Kconfig"
+
+ source "drivers/soc/Kconfig"
+
++source "drivers/pmdomain/Kconfig"
++
+ source "drivers/devfreq/Kconfig"
+
+ source "drivers/extcon/Kconfig"
+--- /dev/null
++++ b/drivers/pmdomain/Kconfig
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0-only
++menu "PM Domains"
++
++endmenu
--- /dev/null
+From de12fe43dbd0ea9fa980ffa05822bd7fd5eed330 Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+Date: Tue, 12 Sep 2023 13:31:44 +0200
+Subject: [PATCH 044/116] pmdomain: starfive: Move Kconfig file to the pmdomain
+ subsystem
+
+The Kconfig belongs closer to the corresponding implementation, hence let's
+move it from the soc subsystem to the pmdomain subsystem.
+
+Cc: Walker Chen <walker.chen@starfivetech.com>
+Cc: Conor Dooley <conor@kernel.org>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/Kconfig | 2 ++
+ drivers/{soc => pmdomain}/starfive/Kconfig | 0
+ drivers/soc/Kconfig | 1 -
+ 3 files changed, 2 insertions(+), 1 deletion(-)
+ rename drivers/{soc => pmdomain}/starfive/Kconfig (100%)
+
+--- a/drivers/pmdomain/Kconfig
++++ b/drivers/pmdomain/Kconfig
+@@ -1,4 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ menu "PM Domains"
+
++source "drivers/pmdomain/starfive/Kconfig"
++
+ endmenu
+--- a/drivers/soc/Kconfig
++++ b/drivers/soc/Kconfig
+@@ -24,7 +24,6 @@ source "drivers/soc/renesas/Kconfig"
+ source "drivers/soc/rockchip/Kconfig"
+ source "drivers/soc/samsung/Kconfig"
+ source "drivers/soc/sifive/Kconfig"
+-source "drivers/soc/starfive/Kconfig"
+ source "drivers/soc/sunxi/Kconfig"
+ source "drivers/soc/tegra/Kconfig"
+ source "drivers/soc/ti/Kconfig"
+--- /dev/null
++++ b/drivers/pmdomain/starfive/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0
++
++config JH71XX_PMU
++ bool "Support PMU for StarFive JH71XX Soc"
++ depends on PM
++ depends on ARCH_STARFIVE || COMPILE_TEST
++ default ARCH_STARFIVE
++ select PM_GENERIC_DOMAINS
++ help
++ Say 'y' here to enable support power domain support.
++ In order to meet low power requirements, a Power Management Unit (PMU)
++ is designed for controlling power resources in StarFive JH71XX SoCs.
+--- a/drivers/soc/starfive/Kconfig
++++ /dev/null
+@@ -1,12 +0,0 @@
+-# SPDX-License-Identifier: GPL-2.0
+-
+-config JH71XX_PMU
+- bool "Support PMU for StarFive JH71XX Soc"
+- depends on PM
+- depends on ARCH_STARFIVE || COMPILE_TEST
+- default ARCH_STARFIVE
+- select PM_GENERIC_DOMAINS
+- help
+- Say 'y' here to enable support power domain support.
+- In order to meet low power requirements, a Power Management Unit (PMU)
+- is designed for controlling power resources in StarFive JH71XX SoCs.
--- /dev/null
+From cac9ce9c7f388a741389b1ec47af65420254db55 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 27 Sep 2023 06:07:33 -0700
+Subject: [PATCH 045/116] dt-bindings: power: Update prefixes for AON power
+ domain
+
+Use "JH7110_AON_PD_" prefix for AON power domain for JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://lore.kernel.org/r/20230927130734.9921-2-changhuang.liang@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ include/dt-bindings/power/starfive,jh7110-pmu.h | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/include/dt-bindings/power/starfive,jh7110-pmu.h
++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h
+@@ -14,7 +14,8 @@
+ #define JH7110_PD_ISP 5
+ #define JH7110_PD_VENC 6
+
+-#define JH7110_PD_DPHY_TX 0
+-#define JH7110_PD_DPHY_RX 1
++/* AON Power Domain */
++#define JH7110_AON_PD_DPHY_TX 0
++#define JH7110_AON_PD_DPHY_RX 1
+
+ #endif
--- /dev/null
+From 3ea89ffbd6cc5a15acca6bc2130572f8bd85b9d4 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 27 Sep 2023 06:07:34 -0700
+Subject: [PATCH 046/116] pmdomain: starfive: Update prefixes for AON power
+ domain
+
+Use "JH7110_AON_PD_" prefix for AON power doamin for JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Link: https://lore.kernel.org/r/20230927130734.9921-3-changhuang.liang@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -420,11 +420,11 @@ static const struct jh71xx_pmu_match_dat
+ };
+
+ static const struct jh71xx_domain_info jh7110_aon_power_domains[] = {
+- [JH7110_PD_DPHY_TX] = {
++ [JH7110_AON_PD_DPHY_TX] = {
+ .name = "DPHY-TX",
+ .bit = 30,
+ },
+- [JH7110_PD_DPHY_RX] = {
++ [JH7110_AON_PD_DPHY_RX] = {
+ .name = "DPHY-RX",
+ .bit = 31,
+ },
--- /dev/null
+From e7e3d62b7a470ddf15e30574232b52b2e23ba606 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Mon, 21 Aug 2023 22:41:50 +0800
+Subject: [PATCH 047/116] riscv: dts: starfive: pinfunc: Fix the pins name of
+ I2STX1
+
+These pins are actually I2STX1 clock input, not I2STX0,
+so their names should be changed.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h
++++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h
+@@ -240,8 +240,8 @@
+ #define GPI_SYS_MCLK_EXT 30
+ #define GPI_SYS_I2SRX_BCLK 31
+ #define GPI_SYS_I2SRX_LRCK 32
+-#define GPI_SYS_I2STX0_BCLK 33
+-#define GPI_SYS_I2STX0_LRCK 34
++#define GPI_SYS_I2STX1_BCLK 33
++#define GPI_SYS_I2STX1_LRCK 34
+ #define GPI_SYS_TDM_CLK 35
+ #define GPI_SYS_TDM_RXD 36
+ #define GPI_SYS_TDM_SYNC 37
--- /dev/null
+From a3d3f611f31fa2dca3deefa7cd443abca02e03fa Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 11 Apr 2023 16:31:15 +0800
+Subject: [PATCH 048/116] riscv: dts: starfive: Add full support (except VIN
+ and VOUT) for JH7110 and VisionFive 2 board
+
+Merge all StarFive dts patches together except VIN and VOUT.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../jh7110-starfive-visionfive-2.dtsi | 199 +++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110.dtsi | 233 ++++++++++++++++++
+ 2 files changed, 432 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -19,6 +19,8 @@
+ i2c6 = &i2c6;
+ mmc0 = &mmc0;
+ mmc1 = &mmc1;
++ pcie0 = &pcie0;
++ pcie1 = &pcie1;
+ serial0 = &uart0;
+ };
+
+@@ -40,6 +42,33 @@
+ gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
+ priority = <224>;
+ };
++
++ pwmdac_codec: pwmdac-codec {
++ compatible = "linux,spdif-dit";
++ #sound-dai-cells = <0>;
++ };
++
++ sound-pwmdac {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "left_j";
++ bitclock-master = <&sndcpu0>;
++ frame-master = <&sndcpu0>;
++
++ sndcpu0: cpu {
++ sound-dai = <&pwmdac>;
++ };
++
++ codec {
++ sound-dai = <&pwmdac_codec>;
++ };
++ };
++ };
+ };
+
+ &dvp_clk {
+@@ -202,8 +231,28 @@
+ status = "okay";
+ };
+
++&i2srx {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2srx_pins>;
++ status = "okay";
++};
++
++&i2stx0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&mclk_ext_pins>;
++ status = "okay";
++};
++
++&i2stx1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2stx1_pins>;
++ status = "okay";
++};
++
+ &mmc0 {
+ max-frequency = <100000000>;
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
+ bus-width = <8>;
+ cap-mmc-highspeed;
+ mmc-ddr-1_8v;
+@@ -220,6 +269,8 @@
+
+ &mmc1 {
+ max-frequency = <100000000>;
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>;
++ assigned-clock-rates = <50000000>;
+ bus-width = <4>;
+ no-sdio;
+ no-mmc;
+@@ -231,6 +282,34 @@
+ status = "okay";
+ };
+
++&pcie0 {
++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>;
++ phys = <&pciephy0>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pcie0_pins>;
++ status = "okay";
++};
++
++&pcie1 {
++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>;
++ phys = <&pciephy1>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pcie1_pins>;
++ status = "okay";
++};
++
++&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pins>;
++ status = "okay";
++};
++
++&pwmdac {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwmdac_pins>;
++ status = "okay";
++};
++
+ &qspi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -336,6 +415,46 @@
+ };
+ };
+
++ i2srx_pins: i2srx-0 {
++ clk-sd-pins {
++ pinmux = <GPIOMUX(38, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_BCLK)>,
++ <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_LRCK)>,
++ <GPIOMUX(38, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2STX1_BCLK)>,
++ <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2STX1_LRCK)>,
++ <GPIOMUX(61, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_SDIN0)>;
++ input-enable;
++ };
++ };
++
++ i2stx1_pins: i2stx1-0 {
++ sd-pins {
++ pinmux = <GPIOMUX(44, GPOUT_SYS_I2STX1_SDO0,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ };
++ };
++
++ mclk_ext_pins: mclk-ext-0 {
++ mclk-ext-pins {
++ pinmux = <GPIOMUX(4, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_MCLK_EXT)>;
++ input-enable;
++ };
++ };
++
+ mmc0_pins: mmc0-0 {
+ rst-pins {
+ pinmux = <GPIOMUX(62, GPOUT_SYS_SDIO0_RST,
+@@ -400,6 +519,86 @@
+ slew-rate = <0>;
+ };
+ };
++
++ pcie0_pins: pcie0-0 {
++ clkreq-pins {
++ pinmux = <GPIOMUX(27, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_NONE)>;
++ bias-pull-down;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ wake-pins {
++ pinmux = <GPIOMUX(32, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_NONE)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
++
++ pcie1_pins: pcie1-0 {
++ clkreq-pins {
++ pinmux = <GPIOMUX(29, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_NONE)>;
++ bias-pull-down;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ wake-pins {
++ pinmux = <GPIOMUX(21, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_NONE)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
++
++ pwm_pins: pwm-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL0,
++ GPOEN_SYS_PWM0_CHANNEL0,
++ GPI_NONE)>,
++ <GPIOMUX(59, GPOUT_SYS_PWM_CHANNEL1,
++ GPOEN_SYS_PWM0_CHANNEL1,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
++
++ pwmdac_pins: pwmdac-0 {
++ pwmdac-pins {
++ pinmux = <GPIOMUX(33, GPOUT_SYS_PWMDAC_LEFT,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(34, GPOUT_SYS_PWMDAC_RIGHT,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <2>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
+
+ spi0_pins: spi0-0 {
+ mosi-pins {
+--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
+@@ -244,6 +244,7 @@
+ clock-output-names = "dvp_clk";
+ #clock-cells = <0>;
+ };
++
+ gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock {
+ compatible = "fixed-clock";
+ clock-output-names = "gmac0_rgmii_rxin";
+@@ -512,6 +513,43 @@
+ status = "disabled";
+ };
+
++ pwmdac: pwmdac@100b0000 {
++ compatible = "starfive,jh7110-pwmdac";
++ reg = <0x0 0x100b0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_PWMDAC_APB>,
++ <&syscrg JH7110_SYSCLK_PWMDAC_CORE>;
++ clock-names = "apb", "core";
++ resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>;
++ dmas = <&dma 22>;
++ dma-names = "tx";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ i2srx: i2s@100e0000 {
++ compatible = "starfive,jh7110-i2srx";
++ reg = <0x0 0x100e0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
++ <&syscrg JH7110_SYSCLK_I2SRX_APB>,
++ <&syscrg JH7110_SYSCLK_MCLK>,
++ <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++ <&mclk_ext>,
++ <&syscrg JH7110_SYSCLK_I2SRX_BCLK>,
++ <&syscrg JH7110_SYSCLK_I2SRX_LRCK>,
++ <&i2srx_bclk_ext>,
++ <&i2srx_lrck_ext>;
++ clock-names = "i2sclk", "apb", "mclk",
++ "mclk_inner", "mclk_ext", "bclk",
++ "lrck", "bclk_ext", "lrck_ext";
++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
++ dmas = <0>, <&dma 24>;
++ dma-names = "tx", "rx";
++ starfive,syscon = <&sys_syscon 0x18 0x2>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
+ usb0: usb@10100000 {
+ compatible = "starfive,jh7110-usb";
+ ranges = <0x0 0x0 0x10100000 0x100000>;
+@@ -736,6 +774,56 @@
+ status = "disabled";
+ };
+
++ i2stx0: i2s@120b0000 {
++ compatible = "starfive,jh7110-i2stx0";
++ reg = <0x0 0x120b0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>,
++ <&syscrg JH7110_SYSCLK_I2STX0_APB>,
++ <&syscrg JH7110_SYSCLK_MCLK>,
++ <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++ <&mclk_ext>;
++ clock-names = "i2sclk", "apb", "mclk",
++ "mclk_inner","mclk_ext";
++ resets = <&syscrg JH7110_SYSRST_I2STX0_APB>,
++ <&syscrg JH7110_SYSRST_I2STX0_BCLK>;
++ dmas = <&dma 47>;
++ dma-names = "tx";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ i2stx1: i2s@120c0000 {
++ compatible = "starfive,jh7110-i2stx1";
++ reg = <0x0 0x120c0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>,
++ <&syscrg JH7110_SYSCLK_I2STX1_APB>,
++ <&syscrg JH7110_SYSCLK_MCLK>,
++ <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++ <&mclk_ext>,
++ <&syscrg JH7110_SYSCLK_I2STX1_BCLK>,
++ <&syscrg JH7110_SYSCLK_I2STX1_LRCK>,
++ <&i2stx_bclk_ext>,
++ <&i2stx_lrck_ext>;
++ clock-names = "i2sclk", "apb", "mclk",
++ "mclk_inner", "mclk_ext", "bclk",
++ "lrck", "bclk_ext", "lrck_ext";
++ resets = <&syscrg JH7110_SYSRST_I2STX1_APB>,
++ <&syscrg JH7110_SYSRST_I2STX1_BCLK>;
++ dmas = <&dma 48>;
++ dma-names = "tx";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ pwm: pwm@120d0000 {
++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
++ reg = <0x0 0x120d0000 0x0 0x10000>;
++ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
++ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
++ #pwm-cells = <3>;
++ status = "disabled";
++ };
++
+ sfctemp: temperature-sensor@120e0000 {
+ compatible = "starfive,jh7110-temp";
+ reg = <0x0 0x120e0000 0x0 0x10000>;
+@@ -811,6 +899,26 @@
+ #gpio-cells = <2>;
+ };
+
++ timer@13050000 {
++ compatible = "starfive,jh7110-timer";
++ reg = <0x0 0x13050000 0x0 0x10000>;
++ interrupts = <69>, <70>, <71>, <72>;
++ clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>,
++ <&syscrg JH7110_SYSCLK_TIMER0>,
++ <&syscrg JH7110_SYSCLK_TIMER1>,
++ <&syscrg JH7110_SYSCLK_TIMER2>,
++ <&syscrg JH7110_SYSCLK_TIMER3>;
++ clock-names = "apb", "ch0", "ch1",
++ "ch2", "ch3";
++ resets = <&syscrg JH7110_SYSRST_TIMER_APB>,
++ <&syscrg JH7110_SYSRST_TIMER0>,
++ <&syscrg JH7110_SYSRST_TIMER1>,
++ <&syscrg JH7110_SYSRST_TIMER2>,
++ <&syscrg JH7110_SYSRST_TIMER3>;
++ reset-names = "apb", "ch0", "ch1",
++ "ch2", "ch3";
++ };
++
+ watchdog@13070000 {
+ compatible = "starfive,jh7110-wdt";
+ reg = <0x0 0x13070000 0x0 0x10000>;
+@@ -1011,6 +1119,32 @@
+ #power-domain-cells = <1>;
+ };
+
++ csi2rx: csi-bridge@19800000 {
++ compatible = "starfive,jh7110-csi2rx";
++ reg = <0x0 0x19800000 0x0 0x10000>;
++ clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>,
++ <&ispcrg JH7110_ISPCLK_VIN_APB>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>;
++ clock-names = "sys_clk", "p_clk",
++ "pixel_if0_clk", "pixel_if1_clk",
++ "pixel_if2_clk", "pixel_if3_clk";
++ resets = <&ispcrg JH7110_ISPRST_VIN_SYS>,
++ <&ispcrg JH7110_ISPRST_VIN_APB>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>;
++ reset-names = "sys", "reg_bank",
++ "pixel_if0", "pixel_if1",
++ "pixel_if2", "pixel_if3";
++ phys = <&csi_phy>;
++ phy-names = "dphy";
++ status = "disabled";
++ };
++
+ ispcrg: clock-controller@19810000 {
+ compatible = "starfive,jh7110-ispcrg";
+ reg = <0x0 0x19810000 0x0 0x10000>;
+@@ -1028,6 +1162,19 @@
+ power-domains = <&pwrc JH7110_PD_ISP>;
+ };
+
++ csi_phy: phy@19820000 {
++ compatible = "starfive,jh7110-dphy-rx";
++ reg = <0x0 0x19820000 0x0 0x10000>;
++ clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>,
++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>,
++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>;
++ clock-names = "cfg", "ref", "tx";
++ resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>,
++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>;
++ power-domains = <&aon_syscon JH7110_AON_PD_DPHY_RX>;
++ #phy-cells = <0>;
++ };
++
+ voutcrg: clock-controller@295c0000 {
+ compatible = "starfive,jh7110-voutcrg";
+ reg = <0x0 0x295c0000 0x0 0x10000>;
+@@ -1045,5 +1192,91 @@
+ #reset-cells = <1>;
+ power-domains = <&pwrc JH7110_PD_VOUT>;
+ };
++
++ pcie0: pcie@940000000 {
++ compatible = "starfive,jh7110-pcie";
++ reg = <0x9 0x40000000 0x0 0x1000000>,
++ <0x0 0x2b000000 0x0 0x100000>;
++ reg-names = "cfg", "apb";
++ linux,pci-domain = <0>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++ #interrupt-cells = <1>;
++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>,
++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>;
++ interrupts = <56>;
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>,
++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>,
++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>,
++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>;
++ msi-controller;
++ device_type = "pci";
++ starfive,stg-syscon = <&stg_syscon>;
++ bus-range = <0x0 0xff>;
++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>,
++ <&stgcrg JH7110_STGCLK_PCIE0_TL>,
++ <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>,
++ <&stgcrg JH7110_STGCLK_PCIE0_APB>;
++ clock-names = "noc", "tl", "axi_mst0", "apb";
++ resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>,
++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>,
++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>,
++ <&stgcrg JH7110_STGRST_PCIE0_BRG>,
++ <&stgcrg JH7110_STGRST_PCIE0_CORE>,
++ <&stgcrg JH7110_STGRST_PCIE0_APB>;
++ reset-names = "mst0", "slv0", "slv", "brg",
++ "core", "apb";
++ status = "disabled";
++
++ pcie_intc0: interrupt-controller {
++ #address-cells = <0>;
++ #interrupt-cells = <1>;
++ interrupt-controller;
++ };
++ };
++
++ pcie1: pcie@9c0000000 {
++ compatible = "starfive,jh7110-pcie";
++ reg = <0x9 0xc0000000 0x0 0x1000000>,
++ <0x0 0x2c000000 0x0 0x100000>;
++ reg-names = "cfg", "apb";
++ linux,pci-domain = <1>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++ #interrupt-cells = <1>;
++ ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>,
++ <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>;
++ interrupts = <57>;
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>,
++ <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>,
++ <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>,
++ <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>;
++ msi-controller;
++ device_type = "pci";
++ starfive,stg-syscon = <&stg_syscon>;
++ bus-range = <0x0 0xff>;
++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>,
++ <&stgcrg JH7110_STGCLK_PCIE1_TL>,
++ <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>,
++ <&stgcrg JH7110_STGCLK_PCIE1_APB>;
++ clock-names = "noc", "tl", "axi_mst0", "apb";
++ resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>,
++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>,
++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>,
++ <&stgcrg JH7110_STGRST_PCIE1_BRG>,
++ <&stgcrg JH7110_STGRST_PCIE1_CORE>,
++ <&stgcrg JH7110_STGRST_PCIE1_APB>;
++ reset-names = "mst0", "slv0", "slv", "brg",
++ "core", "apb";
++ status = "disabled";
++
++ pcie_intc1: interrupt-controller {
++ #address-cells = <0>;
++ #interrupt-cells = <1>;
++ interrupt-controller;
++ };
++ };
+ };
+ };
--- /dev/null
+From ae7b57a0c69953f5ec06a378aedeed4c86637998 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 11 Apr 2023 16:25:57 +0800
+Subject: [PATCH 049/116] MAINTAINERS: Update all StarFive entries
+
+Merge all StarFive maintainers changes together.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ MAINTAINERS | 61 +++++++++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 57 insertions(+), 4 deletions(-)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7053,6 +7053,14 @@ T: git git://anongit.freedesktop.org/drm
+ F: Documentation/devicetree/bindings/display/rockchip/
+ F: drivers/gpu/drm/rockchip/
+
++DRM DRIVERS FOR STARFIVE
++M: Keith Zhao <keith.zhao@starfivetech.com>
++L: dri-devel@lists.freedesktop.org
++S: Maintained
++T: git git://anongit.freedesktop.org/drm/drm-misc
++F: Documentation/devicetree/bindings/display/starfive/
++F: drivers/gpu/drm/verisilicon/
++
+ DRM DRIVERS FOR STI
+ M: Alain Volmat <alain.volmat@foss.st.com>
+ L: dri-devel@lists.freedesktop.org
+@@ -16016,6 +16024,13 @@ F: Documentation/i2c/busses/i2c-ocores.r
+ F: drivers/i2c/busses/i2c-ocores.c
+ F: include/linux/platform_data/i2c-ocores.h
+
++OPENCORES PWM DRIVER
++M: William Qiu <william.qiu@starfivetech.com>
++M: Hal Feng <hal.feng@starfivetech.com>
++S: Supported
++F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
++F: drivers/pwm/pwm-ocores.c
++
+ OPENRISC ARCHITECTURE
+ M: Jonas Bonn <jonas@southpole.se>
+ M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
+@@ -16427,6 +16442,14 @@ S: Maintained
+ F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt
+ F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
+
++PCI DRIVER FOR PLDA PCIE IP
++M: Daire McNamara <daire.mcnamara@microchip.com>
++M: Kevin Xie <kevin.xie@starfivetech.com>
++L: linux-pci@vger.kernel.org
++S: Maintained
++F: Documentation/devicetree/bindings/pci/plda,*
++F: drivers/pci/controller/plda/*plda*
++
+ PCI DRIVER FOR RENESAS R-CAR
+ M: Marek Vasut <marek.vasut+renesas@gmail.com>
+ M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+@@ -16658,7 +16681,7 @@ M: Daire McNamara <daire.mcnamara@microc
+ L: linux-pci@vger.kernel.org
+ S: Supported
+ F: Documentation/devicetree/bindings/pci/microchip*
+-F: drivers/pci/controller/*microchip*
++F: drivers/pci/controller/plda/*microchip*
+
+ PCIE DRIVER FOR QUALCOMM MSM
+ M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+@@ -16682,6 +16705,13 @@ S: Maintained
+ F: Documentation/devicetree/bindings/pci/socionext,uniphier-pcie*
+ F: drivers/pci/controller/dwc/pcie-uniphier*
+
++PCIE DRIVER FOR STARFIVE JH71x0
++M: Kevin Xie <kevin.xie@starfivetech.com>
++L: linux-pci@vger.kernel.org
++S: Maintained
++F: Documentation/devicetree/bindings/pci/starfive*
++F: drivers/pci/controller/plda/pcie-starfive.c
++
+ PCIE DRIVER FOR ST SPEAR13XX
+ M: Pratyush Anand <pratyush.anand@gmail.com>
+ L: linux-pci@vger.kernel.org
+@@ -18454,7 +18484,7 @@ F: drivers/char/hw_random/mpfs-rng.c
+ F: drivers/clk/microchip/clk-mpfs*.c
+ F: drivers/i2c/busses/i2c-microchip-corei2c.c
+ F: drivers/mailbox/mailbox-mpfs.c
+-F: drivers/pci/controller/pcie-microchip-host.c
++F: drivers/pci/controller/plda/pcie-microchip-host.c
+ F: drivers/pwm/pwm-microchip-core.c
+ F: drivers/reset/reset-mpfs.c
+ F: drivers/rtc/rtc-mpfs.c
+@@ -20435,6 +20465,15 @@ M: Ion Badulescu <ionut@badula.org>
+ S: Odd Fixes
+ F: drivers/net/ethernet/adaptec/starfire*
+
++STARFIVE CAMERA SUBSYSTEM DRIVER
++M: Jack Zhu <jack.zhu@starfivetech.com>
++M: Changhuang Liang <changhuang.liang@starfivetech.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++F: Documentation/admin-guide/media/starfive_camss.rst
++F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
++F: drivers/staging/media/starfive/camss
++
+ STARFIVE CRYPTO DRIVER
+ M: Jia Jie Ho <jiajie.ho@starfivetech.com>
+ M: William Qiu <william.qiu@starfivetech.com>
+@@ -20473,6 +20512,13 @@ S: Supported
+ F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml
+ F: drivers/clk/starfive/clk-starfive-jh7110-pll.c
+
++STARFIVE JH7110 PWMDAC DRIVER
++M: Hal Feng <hal.feng@starfivetech.com>
++M: Xingyu Wu <xingyu.wu@starfivetech.com>
++S: Supported
++F: Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
++F: sound/soc/starfive/jh7110_pwmdac.c
++
+ STARFIVE JH7110 SYSCON
+ M: William Qiu <william.qiu@starfivetech.com>
+ M: Xingyu Wu <xingyu.wu@starfivetech.com>
+@@ -20520,9 +20566,10 @@ F: drivers/usb/cdns3/cdns3-starfive.c
+
+ STARFIVE JH71XX PMU CONTROLLER DRIVER
+ M: Walker Chen <walker.chen@starfivetech.com>
++M: Changhuang Liang <changhuang.liang@starfivetech.com>
+ S: Supported
+ F: Documentation/devicetree/bindings/power/starfive*
+-F: drivers/pmdomain/starfive/jh71xx-pmu.c
++F: drivers/pmdomain/starfive/
+ F: include/dt-bindings/power/starfive,jh7110-pmu.h
+
+ STARFIVE SOC DRIVERS
+@@ -20530,7 +20577,13 @@ M: Conor Dooley <conor@kernel.org>
+ S: Maintained
+ T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
+ F: Documentation/devicetree/bindings/soc/starfive/
+-F: drivers/soc/starfive/
++
++STARFIVE JH7110 TIMER DRIVER
++M: Samin Guo <samin.guo@starfivetech.com>
++M: Xingyu Wu <xingyu.wu@starfivetech.com>
++S: Supported
++F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
++F: drivers/clocksource/timer-jh7110.c
+
+ STARFIVE TRNG DRIVER
+ M: Jia Jie Ho <jiajie.ho@starfivetech.com>
--- /dev/null
+From e394195396995456ef98f52ac123c0cb64687748 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Mon, 9 Oct 2023 10:59:03 +0800
+Subject: [PATCH 050/116] mmc: starfive: Change the voltage to adapt to JH7110
+ EVB
+
+Change the voltage, so the driver can adapt to JH7110 EVB.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/mmc/host/dw_mmc-starfive.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+--- a/drivers/mmc/host/dw_mmc-starfive.c
++++ b/drivers/mmc/host/dw_mmc-starfive.c
+@@ -8,6 +8,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
++#include <linux/gpio.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/mmc/host.h>
+ #include <linux/module.h>
+@@ -95,10 +96,39 @@ out:
+ return ret;
+ }
+
++static int dw_mci_starfive_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++
++ struct dw_mci_slot *slot = mmc_priv(mmc);
++ struct dw_mci *host = slot->host;
++ u32 ret;
++
++ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
++ ret = gpio_direction_output(25, 0);
++ else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
++ ret = gpio_direction_output(25, 1);
++ if (ret)
++ return ret;
++
++ if (!IS_ERR(mmc->supply.vqmmc)) {
++ ret = mmc_regulator_set_vqmmc(mmc, ios);
++ if (ret < 0) {
++ dev_err(host->dev, "Regulator set error %d\n", ret);
++ return ret;
++ }
++ }
++
++ /* We should delay 20ms wait for timing setting finished. */
++ mdelay(20);
++
++ return 0;
++}
++
+ static const struct dw_mci_drv_data starfive_data = {
+ .common_caps = MMC_CAP_CMD23,
+ .set_ios = dw_mci_starfive_set_ios,
+ .execute_tuning = dw_mci_starfive_execute_tuning,
++ .switch_voltage = dw_mci_starfive_switch_voltage,
+ };
+
+ static const struct of_device_id dw_mci_starfive_match[] = {
--- /dev/null
+From 2cd3e51cb76d49d8db6274ebdc1ba1eb5c872f10 Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfivetech.com>
+Date: Sun, 4 Feb 2024 10:35:24 +0800
+Subject: [PATCH 051/116] spi: spl022: Get and deassert reset in probe()
+
+This fix spi1~6 communication time out.
+
+Signed-off-by: ziv.xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -33,6 +33,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/of.h>
+ #include <linux/pinctrl/consumer.h>
++#include <linux/reset.h>
+
+ /*
+ * This macro is used to define some register default values.
+@@ -370,6 +371,7 @@ struct pl022 {
+ resource_size_t phybase;
+ void __iomem *virtbase;
+ struct clk *clk;
++ struct reset_control *rst;
+ struct spi_controller *host;
+ struct pl022_ssp_controller *host_info;
+ /* Message per-transfer pump */
+@@ -2181,6 +2183,19 @@ static int pl022_probe(struct amba_devic
+ goto err_no_clk_en;
+ }
+
++ pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++ if (IS_ERR(pl022->rst)) {
++ status = PTR_ERR(pl022->rst);
++ dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
++ goto err_no_rst;
++ }
++
++ status = reset_control_deassert(pl022->rst);
++ if (status) {
++ dev_err(&adev->dev, "could not deassert SSP/SPI bus reset\n");
++ goto err_no_rst_de;
++ }
++
+ /* Initialize transfer pump */
+ tasklet_init(&pl022->pump_transfers, pump_transfers,
+ (unsigned long)pl022);
+@@ -2240,6 +2255,8 @@ static int pl022_probe(struct amba_devic
+ if (platform_info->enable_dma)
+ pl022_dma_remove(pl022);
+ err_no_irq:
++ err_no_rst_de:
++ err_no_rst:
+ clk_disable_unprepare(pl022->clk);
+ err_no_clk_en:
+ err_no_clk:
--- /dev/null
+From 9cc8de0cdc1600f460f618e342e1f524adad07c4 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Wed, 21 Feb 2024 10:23:48 +0800
+Subject: [PATCH 052/116] ASoC: dwc: i2s: Fix getting platform data error for
+ StarFive JH7110
+
+JH7110 need to use a DT specific function to get the platform data,
+otherwise, it fails in probe().
+
+Fixes: 9c97790a07dc ("ASoC: dwc: Fix non-DT instantiation")
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -917,7 +917,11 @@ static int jh7110_i2stx0_clk_cfg(struct
+
+ static int dw_i2s_probe(struct platform_device *pdev)
+ {
++#ifdef CONFIG_OF
++ const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev);
++#else
+ const struct i2s_platform_data *pdata = pdev->dev.platform_data;
++#endif
+ struct dw_i2s_dev *dev;
+ struct resource *res;
+ int ret, irq;
--- /dev/null
+From 1be9bd37fdb5f50162dba0158e1fee295ebca9aa Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Tue, 17 Oct 2023 17:22:52 +0800
+Subject: [PATCH 053/116] ASoC: dwc: i2s: Add RX master support for StarFive
+ JH7110 SoC
+
+Add JH7110 I2S RX master support, so the PDM can work on JH7110
+EVB board.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -906,6 +906,27 @@ static int jh7110_i2srx_crg_init(struct
+ return jh7110_i2s_crg_slave_init(dev);
+ }
+
++/* Special syscon initialization about RX channel with master mode on JH7110 SoC */
++static int jh7110_i2srx_mst_crg_init(struct dw_i2s_dev *dev)
++{
++ struct regmap *regmap;
++ unsigned int args[5];
++
++ regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node,
++ "starfive,syscon",
++ 5, args);
++ if (IS_ERR(regmap))
++ return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n");
++
++ /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */
++ regmap_update_bits(regmap, args[0], args[1], args[1]);
++
++ /* Change I2Srx source (PDM) with syscon register, args[0]: offset, args[1]: mask */
++ regmap_update_bits(regmap, args[2], args[3], args[4]);
++
++ return jh7110_i2s_crg_master_init(dev);
++}
++
+ static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config)
+ {
+ struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config);
+@@ -1086,11 +1107,21 @@ static const struct i2s_platform_data jh
+ .i2s_pd_init = jh7110_i2srx_crg_init,
+ };
+
++static const struct i2s_platform_data jh7110_i2srx_mst_data = {
++ .cap = DWC_I2S_RECORD | DW_I2S_MASTER,
++ .channel = TWO_CHANNEL_SUPPORT,
++ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
++ .snd_rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000,
++ .i2s_clk_cfg = jh7110_i2stx0_clk_cfg,
++ .i2s_pd_init = jh7110_i2srx_mst_crg_init,
++};
++
+ static const struct of_device_id dw_i2s_of_match[] = {
+ { .compatible = "snps,designware-i2s", },
+ { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, },
+ { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,},
+ { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,},
++ { .compatible = "starfive,jh7110-i2srx-master", .data = &jh7110_i2srx_mst_data,},
+ {},
+ };
+
--- /dev/null
+From 1ec26ba377d8ae59cd09811ec78623a750a9c150 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 26 Feb 2024 11:35:44 +0800
+Subject: [PATCH 054/116] pinctrl: starfive: jh7110: Unset .strict in
+ pinmux_ops
+
+Allow simultaneous use of the same pin for GPIO and another function.
+This feature is used in HDMI hot plug detect.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c
++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c
+@@ -327,7 +327,6 @@ static const struct pinmux_ops jh7110_pi
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = jh7110_set_mux,
+- .strict = true,
+ };
+
+ static const u8 jh7110_drive_strength_mA[4] = { 2, 4, 8, 12 };
--- /dev/null
+From 005549a2bd839335b0e3dc4152f00f642b524f07 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Sat, 7 Oct 2023 18:10:20 +0800
+Subject: [PATCH 055/116] mm/pgtable-generic.c: Export symbol __pte_offset_map
+
+So JH7110 vdec can call pte_offset_map() when it is built as a module.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ mm/pgtable-generic.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/mm/pgtable-generic.c
++++ b/mm/pgtable-generic.c
+@@ -304,6 +304,7 @@ nomap:
+ rcu_read_unlock();
+ return NULL;
+ }
++EXPORT_SYMBOL(__pte_offset_map);
+
+ pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd,
+ unsigned long addr, spinlock_t **ptlp)
--- /dev/null
+From 9f46b0d43f8945ff3a8b81ddc6567df370b60911 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 28 Jul 2023 17:19:12 +0800
+Subject: [PATCH 056/116] riscv: dts: starfive: Add JH7110 EVB device tree
+
+Add JH7110 evaluation board device tree.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile | 3 +
+ arch/riscv/boot/dts/starfive/jh7110-clk.dtsi | 80 ++
+ .../boot/dts/starfive/jh7110-evb-pinctrl.dtsi | 997 ++++++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110-evb.dts | 35 +
+ arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 854 +++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110.dtsi | 482 ++++++++-
+ 6 files changed, 2444 insertions(+), 7 deletions(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-clk.dtsi
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -4,9 +4,12 @@ DTC_FLAGS_jh7100-beaglev-starlight := -@
+ DTC_FLAGS_jh7100-starfive-visionfive-v1 := -@
+ DTC_FLAGS_jh7110-starfive-visionfive-2-v1.2a := -@
+ DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@
++DTC_FLAGS_jh7110-evb := -@
+
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
++
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-clk.dtsi
+@@ -0,0 +1,80 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/ {
++ ac108_mclk: ac108_mclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ clk_ext_camera: clk-ext-camera {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ wm8960_mclk: wm8960_mclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24576000>;
++ };
++};
++
++&dvp_clk {
++ clock-frequency = <74250000>;
++};
++
++&gmac0_rgmii_rxin {
++ clock-frequency = <125000000>;
++};
++
++&gmac0_rmii_refin {
++ clock-frequency = <50000000>;
++};
++
++&gmac1_rgmii_rxin {
++ clock-frequency = <125000000>;
++};
++
++&gmac1_rmii_refin {
++ clock-frequency = <50000000>;
++};
++
++&hdmitx0_pixelclk {
++ clock-frequency = <297000000>;
++};
++
++&i2srx_bclk_ext {
++ clock-frequency = <12288000>;
++};
++
++&i2srx_lrck_ext {
++ clock-frequency = <192000>;
++};
++
++&i2stx_bclk_ext {
++ clock-frequency = <12288000>;
++};
++
++&i2stx_lrck_ext {
++ clock-frequency = <192000>;
++};
++
++&mclk_ext {
++ clock-frequency = <12288000>;
++};
++
++&osc {
++ clock-frequency = <24000000>;
++};
++
++&rtc_osc {
++ clock-frequency = <32768>;
++};
++
++&tdm_ext {
++ clock-frequency = <49152000>;
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi
+@@ -0,0 +1,997 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ * Author: Hal Feng <hal.feng@starfivetech.com>
++ */
++
++#include "jh7110-pinfunc.h"
++
++&sysgpio {
++ can0_pins: can0-0 {
++ can-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_CAN0_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_CAN0_RXD)>,
++ <GPIOMUX(32, GPOUT_SYS_CAN0_STBY,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ can1_pins: can1-0 {
++ can-pins {
++ pinmux = <GPIOMUX(29, GPOUT_SYS_CAN1_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(27, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_CAN1_RXD)>,
++ <GPIOMUX(45, GPOUT_SYS_CAN1_STBY,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ input-enable;
++ };
++ };
++
++ dvp_pins: dvp-0 {
++ dvp-pins{
++ pinmux = <PINMUX(21, 2)>,
++ <PINMUX(22, 2)>,
++ <PINMUX(23, 2)>,
++ <PINMUX(24, 2)>,
++ <PINMUX(25, 2)>,
++ <PINMUX(26, 2)>,
++ <PINMUX(27, 2)>,
++ <PINMUX(28, 2)>,
++ <PINMUX(29, 2)>,
++ <PINMUX(30, 2)>,
++ <PINMUX(31, 2)>,
++ <PINMUX(32, 2)>,
++ <PINMUX(33, 2)>,
++ <PINMUX(34, 2)>,
++ <PINMUX(35, 2)>;
++ input-enable;
++ };
++ };
++
++ emmc0_pins: emmc0-0 {
++ emmc-pins {
++ pinmux = <GPIOMUX(22, GPOUT_SYS_SDIO0_RST,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <PINMUX(64, 0)>,
++ <PINMUX(65, 0)>,
++ <PINMUX(66, 0)>,
++ <PINMUX(67, 0)>,
++ <PINMUX(68, 0)>,
++ <PINMUX(69, 0)>,
++ <PINMUX(70, 0)>,
++ <PINMUX(71, 0)>,
++ <PINMUX(72, 0)>,
++ <PINMUX(73, 0)>;
++ bias-pull-up;
++ drive-strength = <12>;
++ input-enable;
++ slew-rate = <1>;
++ };
++ };
++
++ emmc1_pins: emmc1-0 {
++ emmc-pins {
++ pinmux = <GPIOMUX(51, GPOUT_SYS_SDIO1_RST,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(38, GPOUT_SYS_SDIO1_CLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(36, GPOUT_SYS_SDIO1_CMD,
++ GPOEN_SYS_SDIO1_CMD,
++ GPI_SYS_SDIO1_CMD)>,
++ <GPIOMUX(43, GPOUT_SYS_SDIO1_DATA0,
++ GPOEN_SYS_SDIO1_DATA0,
++ GPI_SYS_SDIO1_DATA0)>,
++ <GPIOMUX(48, GPOUT_SYS_SDIO1_DATA1,
++ GPOEN_SYS_SDIO1_DATA1,
++ GPI_SYS_SDIO1_DATA1)>,
++ <GPIOMUX(53, GPOUT_SYS_SDIO1_DATA2,
++ GPOEN_SYS_SDIO1_DATA2,
++ GPI_SYS_SDIO1_DATA2)>,
++ <GPIOMUX(63, GPOUT_SYS_SDIO1_DATA3,
++ GPOEN_SYS_SDIO1_DATA3,
++ GPI_SYS_SDIO1_DATA3)>,
++ <GPIOMUX(52, GPOUT_SYS_SDIO1_DATA4,
++ GPOEN_SYS_SDIO1_DATA4,
++ GPI_SYS_SDIO1_DATA4)>,
++ <GPIOMUX(39, GPOUT_SYS_SDIO1_DATA5,
++ GPOEN_SYS_SDIO1_DATA5,
++ GPI_SYS_SDIO1_DATA5)>,
++ <GPIOMUX(46, GPOUT_SYS_SDIO1_DATA6,
++ GPOEN_SYS_SDIO1_DATA6,
++ GPI_SYS_SDIO1_DATA6)>,
++ <GPIOMUX(47, GPOUT_SYS_SDIO1_DATA7,
++ GPOEN_SYS_SDIO1_DATA7,
++ GPI_SYS_SDIO1_DATA7)>;
++ bias-pull-up;
++ input-enable;
++ };
++ };
++
++ gmac0_pins: gmac0-0 {
++ reset-pins {
++ pinmux = <GPIOMUX(13, GPOUT_HIGH,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-pull-up;
++ };
++ };
++
++ gmac1_pins: gmac1-0 {
++ mdc-pins {
++ pinmux = <PINMUX(75, 0)>;
++ };
++ };
++
++ hdmi_pins: hdmi-0 {
++ scl-pins {
++ pinmux = <GPIOMUX(7, GPOUT_SYS_HDMI_DDC_SCL,
++ GPOEN_SYS_HDMI_DDC_SCL,
++ GPI_SYS_HDMI_DDC_SCL)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ sda-pins {
++ pinmux = <GPIOMUX(8, GPOUT_SYS_HDMI_DDC_SDA,
++ GPOEN_SYS_HDMI_DDC_SDA,
++ GPI_SYS_HDMI_DDC_SDA)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ cec-pins {
++ pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
++ GPOEN_SYS_HDMI_CEC_SDA,
++ GPI_SYS_HDMI_CEC_SDA)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ hpd-pins {
++ pinmux = <GPIOMUX(15, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_HDMI_HPD)>;
++ bias-disable; /* external pull-up */
++ input-enable;
++ };
++ };
++
++ i2c0_pins: i2c0-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(57, GPOUT_LOW,
++ GPOEN_SYS_I2C0_CLK,
++ GPI_SYS_I2C0_CLK)>,
++ <GPIOMUX(58, GPOUT_LOW,
++ GPOEN_SYS_I2C0_DATA,
++ GPI_SYS_I2C0_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c1_pins: i2c1-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(49, GPOUT_LOW,
++ GPOEN_SYS_I2C1_CLK,
++ GPI_SYS_I2C1_CLK)>,
++ <GPIOMUX(50, GPOUT_LOW,
++ GPOEN_SYS_I2C1_DATA,
++ GPI_SYS_I2C1_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c2_pins: i2c2-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(11, GPOUT_LOW,
++ GPOEN_SYS_I2C2_CLK,
++ GPI_SYS_I2C2_CLK)>,
++ <GPIOMUX(9, GPOUT_LOW,
++ GPOEN_SYS_I2C2_DATA,
++ GPI_SYS_I2C2_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c3_pins: i2c3-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(51, GPOUT_LOW,
++ GPOEN_SYS_I2C3_CLK,
++ GPI_SYS_I2C3_CLK)>,
++ <GPIOMUX(52, GPOUT_LOW,
++ GPOEN_SYS_I2C3_DATA,
++ GPI_SYS_I2C3_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c4_pins: i2c4-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(18, GPOUT_LOW,
++ GPOEN_SYS_I2C4_CLK,
++ GPI_SYS_I2C4_CLK)>,
++ <GPIOMUX(12, GPOUT_LOW,
++ GPOEN_SYS_I2C4_DATA,
++ GPI_SYS_I2C4_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c5_pins: i2c5-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(19, GPOUT_LOW,
++ GPOEN_SYS_I2C5_CLK,
++ GPI_SYS_I2C5_CLK)>,
++ <GPIOMUX(20, GPOUT_LOW,
++ GPOEN_SYS_I2C5_DATA,
++ GPI_SYS_I2C5_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2c6_pins: i2c6-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(16, GPOUT_LOW,
++ GPOEN_SYS_I2C6_CLK,
++ GPI_SYS_I2C6_CLK)>,
++ <GPIOMUX(17, GPOUT_LOW,
++ GPOEN_SYS_I2C6_DATA,
++ GPI_SYS_I2C6_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ i2s_clk_pins: i2s-clk-0 {
++ bclk-lrck-pins {
++ pinmux = <GPIOMUX(38, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2STX1_BCLK)>,
++ <GPIOMUX(38, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_BCLK)>,
++ <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2STX1_LRCK)>,
++ <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_LRCK)>;
++ input-enable;
++ };
++ };
++
++ i2srx_clk_pins: i2srx-clk-0 {
++ mclk-pins {
++ pinmux = <GPIOMUX(58, GPOUT_SYS_MCLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ i2srx_pins: i2srx-0 {
++ i2srx-pins {
++ pinmux = <GPIOMUX(61, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_I2SRX_SDIN0)>;
++ input-enable;
++ };
++ };
++
++ i2stx_pins: i2stx-0 {
++ i2stx-pins {
++ pinmux = <GPIOMUX(44, GPOUT_SYS_I2STX1_SDO0,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ mclk_ext_pins: mclk-ext-0 {
++ mclk-ext-pins {
++ pinmux = <GPIOMUX(4, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_MCLK_EXT)>;
++ input-enable;
++ };
++ };
++
++ pdm_pins: pdm-0 {
++ pdm-pins {
++ pinmux = <GPIOMUX(54, GPOUT_SYS_PDM_MCLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(60, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_PDM_DMIC0)>;
++ input-enable;
++ };
++ };
++
++ pwm_ch0to3_pins: pwm-ch0to3-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(45, GPOUT_SYS_PWM_CHANNEL0,
++ GPOEN_SYS_PWM0_CHANNEL0,
++ GPI_NONE)>,
++ <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL1,
++ GPOEN_SYS_PWM0_CHANNEL1,
++ GPI_NONE)>,
++ <GPIOMUX(47, GPOUT_SYS_PWM_CHANNEL2,
++ GPOEN_SYS_PWM0_CHANNEL2,
++ GPI_NONE)>,
++ <GPIOMUX(48, GPOUT_SYS_PWM_CHANNEL3,
++ GPOEN_SYS_PWM0_CHANNEL3,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++
++ pwmdac_pins: pwmdac-0 {
++ pwmdac-pins {
++ pinmux = <GPIOMUX(57, GPOUT_SYS_PWMDAC_LEFT,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(42, GPOUT_SYS_PWMDAC_RIGHT,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ };
++ };
++
++ rgb_pad_pins: rgb-pad-pins {
++ rgb-0-pins {
++ pinmux = <PINMUX(36, 1)>;
++ drive-strength = <12>;
++ input-disable;
++ slew-rate = <1>;
++ };
++
++ rgb-pins {
++ pinmux = <PINMUX(37, 1)>,
++ <PINMUX(38, 1)>,
++ <PINMUX(39, 1)>,
++ <PINMUX(40, 1)>,
++ <PINMUX(41, 1)>,
++ <PINMUX(42, 1)>,
++ <PINMUX(43, 1)>,
++ <PINMUX(44, 1)>,
++ <PINMUX(45, 1)>,
++ <PINMUX(46, 1)>,
++ <PINMUX(47, 1)>,
++ <PINMUX(48, 1)>,
++ <PINMUX(49, 1)>,
++ <PINMUX(50, 1)>,
++ <PINMUX(51, 1)>,
++ <PINMUX(52, 1)>,
++ <PINMUX(53, 1)>,
++ <PINMUX(54, 1)>,
++ <PINMUX(55, 1)>,
++ <PINMUX(56, 1)>,
++ <PINMUX(57, 1)>,
++ <PINMUX(58, 1)>,
++ <PINMUX(59, 1)>,
++ <PINMUX(60, 1)>,
++ <PINMUX(61, 1)>,
++ <PINMUX(62, 1)>,
++ <PINMUX(63, 1)>;
++ drive-strength = <12>;
++ input-disable;
++ };
++ };
++
++ sdcard0_pins: sdcard0-0 {
++ sdcard-pins {
++ pinmux = <GPIOMUX(24, GPOUT_SYS_SDIO0_RST,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <PINMUX(64, 0)>,
++ <PINMUX(65, 0)>,
++ <PINMUX(66, 0)>,
++ <PINMUX(67, 0)>,
++ <PINMUX(68, 0)>,
++ <PINMUX(69, 0)>;
++ bias-pull-up;
++ drive-strength = <12>;
++ input-enable;
++ slew-rate = <1>;
++ };
++ };
++
++ sdcard1_pins: sdcard1-0 {
++ sdcard-pins {
++ pinmux = <GPIOMUX(56, GPOUT_SYS_SDIO1_CLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(50, GPOUT_SYS_SDIO1_CMD,
++ GPOEN_SYS_SDIO1_CMD,
++ GPI_SYS_SDIO1_CMD)>,
++ <GPIOMUX(49, GPOUT_SYS_SDIO1_DATA0,
++ GPOEN_SYS_SDIO1_DATA0,
++ GPI_SYS_SDIO1_DATA0)>,
++ <GPIOMUX(45, GPOUT_SYS_SDIO1_DATA1,
++ GPOEN_SYS_SDIO1_DATA1,
++ GPI_SYS_SDIO1_DATA1)>,
++ <GPIOMUX(62, GPOUT_SYS_SDIO1_DATA2,
++ GPOEN_SYS_SDIO1_DATA2,
++ GPI_SYS_SDIO1_DATA2)>,
++ <GPIOMUX(40, GPOUT_SYS_SDIO1_DATA3,
++ GPOEN_SYS_SDIO1_DATA3,
++ GPI_SYS_SDIO1_DATA3)>;
++ bias-pull-up;
++ input-enable;
++ };
++ };
++
++ spdif_pins: spdif-0 {
++ spdif-pins {
++ pinmux = <GPIOMUX(57, GPOUT_SYS_SPDIF,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-pull-up;
++ input-enable;
++ };
++ };
++
++ spi0_pins: spi0-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(38, GPOUT_SYS_SPI0_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(39, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI0_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(36, GPOUT_SYS_SPI0_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI0_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(37, GPOUT_SYS_SPI0_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI0_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi1_pins: spi1-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(42, GPOUT_SYS_SPI1_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(43, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI1_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(40, GPOUT_SYS_SPI1_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI1_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(41, GPOUT_SYS_SPI1_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI1_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi2_pins: spi2-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(46, GPOUT_SYS_SPI2_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(47, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI2_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(44, GPOUT_SYS_SPI2_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI2_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(45, GPOUT_SYS_SPI2_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI2_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi3_pins: spi3-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(50, GPOUT_SYS_SPI3_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(51, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI3_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(48, GPOUT_SYS_SPI3_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI3_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(49, GPOUT_SYS_SPI3_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI3_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi4_pins: spi4-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(54, GPOUT_SYS_SPI4_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(55, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI4_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(52, GPOUT_SYS_SPI4_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI4_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(53, GPOUT_SYS_SPI4_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI4_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi5_pins: spi5-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(58, GPOUT_SYS_SPI5_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(59, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI5_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(56, GPOUT_SYS_SPI5_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI5_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(57, GPOUT_SYS_SPI5_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI5_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ spi6_pins: spi6-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(62, GPOUT_SYS_SPI6_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ miso-pins {
++ pinmux = <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_SPI6_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++
++ sck-pins {
++ pinmux = <GPIOMUX(60, GPOUT_SYS_SPI6_CLK,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI6_CLK)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++
++ ss-pins {
++ pinmux = <GPIOMUX(61, GPOUT_SYS_SPI6_FSS,
++ GPOEN_ENABLE,
++ GPI_SYS_SPI6_FSS)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ tdm_pins: tdm-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(44, GPOUT_SYS_TDM_TXD,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(61, GPOUT_HIGH,
++ GPOEN_DISABLE,
++ GPI_SYS_TDM_RXD)>;
++ input-enable;
++ };
++
++ sync-pins {
++ pinmux = <GPIOMUX(63, GPOUT_HIGH,
++ GPOEN_DISABLE,
++ GPI_SYS_TDM_SYNC)>;
++ input-enable;
++ };
++
++ pcmclk-pins {
++ pinmux = <GPIOMUX(38, GPOUT_HIGH,
++ GPOEN_DISABLE,
++ GPI_SYS_TDM_CLK)>;
++ input-enable;
++ };
++ };
++
++ uart0_pins: uart0-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(5, GPOUT_SYS_UART0_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(6, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART0_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++ };
++
++ uart1_pins: uart1-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_UART1_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART1_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++
++ cts-pins {
++ pinmux = <GPIOMUX(29, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART1_CTS)>;
++ input-enable;
++ };
++
++ rts-pins {
++ pinmux = <GPIOMUX(27, GPOUT_SYS_UART1_RTS,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ uart2_pins: uart2-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_UART2_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART2_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++
++ cts-pins {
++ pinmux = <GPIOMUX(29, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART2_CTS)>;
++ input-enable;
++ };
++
++ rts-pins {
++ pinmux = <GPIOMUX(27, GPOUT_SYS_UART2_RTS,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ uart3_pins: uart3-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_UART3_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART3_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++ };
++
++ uart4_pins: uart4-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_UART4_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART4_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++
++ cts-pins {
++ pinmux = <GPIOMUX(29, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART4_CTS)>;
++ input-enable;
++ };
++
++ rts-pins {
++ pinmux = <GPIOMUX(27, GPOUT_SYS_UART4_RTS,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ uart5_pins: uart5-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(30, GPOUT_SYS_UART5_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(31, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART5_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++
++ cts-pins {
++ pinmux = <GPIOMUX(29, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART5_CTS)>;
++ input-enable;
++ };
++
++ rts-pins {
++ pinmux = <GPIOMUX(27, GPOUT_SYS_UART5_RTS,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ input-enable;
++ };
++ };
++
++ usb_pins: usb-0 {
++ usb-pins {
++ pinmux = <GPIOMUX(33, GPOUT_HIGH,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(34, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_USB_OVERCURRENT)>;
++ input-enable;
++ };
++ };
++};
++
++&aongpio {
++ pwm_ch4to5_pins: pwm-ch4to5-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM4, /* PAD_RGPIO0 */
++ GPOEN_AON_PTC0_OE_N_4,
++ GPI_NONE)>,
++ <GPIOMUX(2, GPOUT_AON_PTC0_PWM5, /* PAD_RGPIO1 */
++ GPOEN_AON_PTC0_OE_N_5,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++
++ pwm_ch6to7_pins: pwm-ch6to7-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM6, /* PAD_RGPIO0 */
++ GPOEN_AON_PTC0_OE_N_6,
++ GPI_NONE)>,
++ <GPIOMUX(2, GPOUT_AON_PTC0_PWM7, /* PAD_RGPIO1 */
++ GPOEN_AON_PTC0_OE_N_7,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ no-sdio;
++ no-mmc;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&usb0 {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+@@ -0,0 +1,854 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110.dtsi"
++#include "jh7110-clk.dtsi"
++#include "jh7110-evb-pinctrl.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++ aliases {
++ ethernet0 = &gmac0;
++ ethernet1 = &gmac1;
++ i2c0 = &i2c0;
++ i2c1 = &i2c1;
++ i2c2 = &i2c2;
++ i2c3 = &i2c3;
++ i2c4 = &i2c4;
++ i2c5 = &i2c5;
++ i2c6 = &i2c6;
++ pcie0 = &pcie0;
++ pcie1 = &pcie1;
++ serial0 = &uart0;
++ serial3 = &uart3;
++ };
++
++ chosen {
++ stdout-path = "serial0:115200n8";
++ };
++
++ cpus {
++ timebase-frequency = <4000000>;
++ };
++
++ memory@40000000 {
++ device_type = "memory";
++ reg = <0x0 0x40000000 0x1 0x0>;
++ };
++
++ reserved-memory {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges;
++
++ linux,cma {
++ compatible = "shared-dma-pool";
++ reusable;
++ size = <0x0 0x20000000>;
++ alignment = <0x0 0x1000>;
++ alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
++ linux,cma-default;
++ };
++
++ e24_mem: e24@c0000000 {
++ reg = <0x0 0x6ce00000 0x0 0x1600000>;
++ };
++
++ xrp_reserved: xrpbuffer@f0000000 {
++ reg = <0x0 0x69c00000 0x0 0x01ffffff
++ 0x0 0x6bc00000 0x0 0x00001000
++ 0x0 0x6bc01000 0x0 0x00fff000
++ 0x0 0x6cc00000 0x0 0x00001000>;
++ };
++ };
++
++ /* i2s + hdmi */
++ sound1: snd-card1 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "i2s";
++ bitclock-master = <&sndi2s0>;
++ frame-master = <&sndi2s0>;
++ mclk-fs = <256>;
++ status = "okay";
++
++ sndi2s0: cpu {
++ sound-dai = <&i2stx0>;
++ };
++
++ codec {
++ sound-dai = <&hdmi>;
++ };
++ };
++ };
++};
++
++&U74_1 {
++ /delete-property/ clocks;
++ /delete-property/ clock-names;
++};
++
++&U74_2 {
++ /delete-property/ clocks;
++ /delete-property/ clock-names;
++};
++
++&U74_3 {
++ /delete-property/ clocks;
++ /delete-property/ clock-names;
++};
++
++&U74_4 {
++ /delete-property/ clocks;
++ /delete-property/ clock-names;
++};
++
++&can0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&can0_pins>;
++ status = "disabled";
++};
++
++&can1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&can1_pins>;
++ status = "disabled";
++};
++
++&co_process {
++ memory-region = <&e24_mem>;
++ status = "okay";
++};
++
++&dc8200 {
++ status = "okay";
++
++ dc_out: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ dc_out_dpi0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&hdmi_input0>;
++ };
++ dc_out_dpi1: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&hdmi_in_lcdc>;
++ };
++ dc_out_dpi2: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&mipi_in>;
++ };
++ };
++};
++
++&display {
++ ports = <&dc_out_dpi0>;
++ status = "okay";
++};
++
++&dsi_output {
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ mipi_in: endpoint {
++ remote-endpoint = <&dc_out_dpi2>;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ mipi_out: endpoint {
++ remote-endpoint = <&dsi_in_port>;
++ };
++ };
++ };
++};
++
++&gmac0 {
++ phy-handle = <&phy0>;
++ phy-mode = "rgmii-id";
++ status = "okay";
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "snps,dwmac-mdio";
++
++ phy0: ethernet-phy@0 {
++ reg = <0>;
++ rx-internal-delay-ps = <1900>;
++ tx-internal-delay-ps = <1650>;
++ };
++ };
++};
++
++&gmac1 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ phy1: ethernet-phy@1 {
++ reg = <0>;
++ rxc-skew-ps = <1060>;
++ txc-skew-ps = <1800>;
++ };
++};
++
++&gpu {
++ status = "okay";
++};
++
++&hdmi {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&hdmi_pins>;
++ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++
++ hdmi_in: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ hdmi_in_lcdc: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&dc_out_dpi1>;
++ };
++ };
++};
++
++&i2c0 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c0_pins>;
++ status = "disabled";
++
++ wm8960: codec@1a {
++ compatible = "wlf,wm8960";
++ reg = <0x1a>;
++ wlf,shared-lrclk;
++ #sound-dai-cells = <0>;
++ };
++
++ ac108: ac108@3b {
++ compatible = "x-power,ac108_0";
++ reg = <0x3b>;
++ #sound-dai-cells = <0>;
++ data-protocol = <0>;
++ };
++};
++
++&i2c1 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c1_pins>;
++ status = "disabled";
++};
++
++&i2c2 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c2_pins>;
++ status = "okay";
++
++ tinker_ft5406: tinker_ft5406@38 {
++ compatible = "tinker_ft5406";
++ reg = <0x38>;
++ };
++
++ seeed_plane_i2c@45 {
++ compatible = "seeed_panel";
++ reg = <0x45>;
++
++ port {
++ panel_dsi_port: endpoint {
++ remote-endpoint = <&dsi_out_port>;
++ };
++ };
++ };
++};
++
++&i2c3 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c3_pins>;
++ status = "disabled";
++};
++
++&i2c4 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c4_pins>;
++ status = "okay";
++
++ sc2235: sc2235@30 {
++ compatible = "smartsens,sc2235";
++ reg = <0x30>;
++ clocks = <&clk_ext_camera>;
++ clock-names = "xclk";
++
++ port {
++ /* Parallel bus endpoint */
++ sc2235_to_parallel: endpoint {
++ remote-endpoint = <¶llel_from_sc2235>;
++ bus-type = <5>; /* Parallel */
++ bus-width = <8>;
++ data-shift = <2>; /* lines 13:6 are used */
++ hsync-active = <1>;
++ vsync-active = <1>;
++ pclk-sample = <1>;
++ };
++ };
++ };
++
++ tda998x@70 {
++ compatible = "nxp,tda998x";
++ reg = <0x70>;
++
++ port {
++ tda998x_0_input: endpoint {
++ remote-endpoint = <&hdmi_out>;
++ };
++ };
++ };
++};
++
++&i2c5 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c5_pins>;
++ status = "okay";
++
++ pmic: jh7110_evb_reg@50 {
++ compatible = "starfive,jh7110-evb-regulator";
++ reg = <0x50>;
++
++ regulators {
++ hdmi_1p8: LDO_REG1 {
++ regulator-name = "hdmi_1p8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ };
++ mipitx_1p8: LDO_REG2 {
++ regulator-name = "mipitx_1p8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ };
++ mipirx_1p8: LDO_REG3 {
++ regulator-name = "mipirx_1p8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ };
++ hdmi_0p9: LDO_REG4 {
++ regulator-name = "hdmi_0p9";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ };
++ mipitx_0p9: LDO_REG5 {
++ regulator-name = "mipitx_0p9";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ };
++ mipirx_0p9: LDO_REG6 {
++ regulator-name = "mipirx_0p9";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ };
++ sdio_vdd: LDO_REG7 {
++ regulator-name = "sdio_vdd";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <3300000>;
++ };
++ };
++ };
++};
++
++&i2c6 {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c6_pins>;
++ status = "okay";
++
++ ov4689: ov4689@36 {
++ compatible = "ovti,ov4689";
++ reg = <0x36>;
++ clocks = <&clk_ext_camera>;
++ clock-names = "xclk";
++ //reset-gpio = <&sysgpio 18 0>;
++ rotation = <180>;
++
++ port {
++ /* Parallel bus endpoint */
++ ov4689_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_ov4689>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <1 2 3 4>;
++ };
++ };
++ };
++
++ imx219: imx219@10 {
++ compatible = "sony,imx219";
++ reg = <0x10>;
++ clocks = <&clk_ext_camera>;
++ clock-names = "xclk";
++ reset-gpio = <&sysgpio 10 0>;
++ //DOVDD-supply = <&v2v8>;
++ rotation = <0>;
++ orientation = <1>; //CAMERA_ORIENTATION_BACK
++
++ port {
++ /* CSI2 bus endpoint */
++ imx219_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_imx219>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <2 1>;
++ lane-polarities = <1 1 1>;
++ link-frequencies = /bits/ 64 <456000000>;
++ };
++ };
++ };
++
++ imx708: imx708@1a {
++ compatible = "sony,imx708";
++ reg = <0x1a>;
++ clocks = <&clk_ext_camera>;
++ reset-gpio = <&sysgpio 10 0>;
++
++ port {
++ imx708_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_imx708>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <450000000>;
++ };
++ };
++ };
++};
++
++&i2srx {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2s_clk_pins &i2srx_pins>;
++};
++
++&i2srx_mst {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2srx_clk_pins>;
++};
++
++&i2stx0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&mclk_ext_pins>;
++ status = "okay";
++};
++
++&i2stx1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2stx_pins>;
++};
++
++&jpu {
++ status = "okay";
++};
++
++&mailbox_contrl0 {
++ status = "okay";
++};
++
++&mailbox_client0 {
++ status = "okay";
++};
++
++&mipi_dphy {
++ status = "okay";
++};
++
++&mipi_dsi {
++ status = "okay";
++
++ port {
++ dsi_out_port: endpoint@0 {
++ remote-endpoint = <&panel_dsi_port>;
++ };
++ dsi_in_port: endpoint@1 {
++ remote-endpoint = <&mipi_out>;
++ };
++ };
++
++ mipi_panel: panel@0 {
++ /*compatible = "";*/
++ status = "okay";
++ };
++};
++
++&pcie0 {
++ enable-gpios = <&sysgpio 32 GPIO_ACTIVE_HIGH>;
++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>;
++ phys = <&pciephy0>;
++ status = "disabled";
++};
++
++&pcie1 {
++ enable-gpios = <&sysgpio 21 GPIO_ACTIVE_HIGH>;
++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>;
++ phys = <&pciephy1>;
++ status = "disabled";
++};
++
++&pciephy0 {
++ starfive,sys-syscon = <&sys_syscon 0x18>;
++ starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>;
++};
++
++&pdm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pdm_pins>;
++ status = "disabled";
++};
++
++&pwmdac {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwmdac_pins>;
++};
++
++&qspi {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ nor_flash: flash@0 {
++ compatible = "jedec,spi-nor";
++ reg=<0>;
++ cdns,read-delay = <5>;
++ spi-max-frequency = <4687500>;
++ cdns,tshsl-ns = <1>;
++ cdns,tsd2d-ns = <1>;
++ cdns,tchsh-ns = <1>;
++ cdns,tslch-ns = <1>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ spl@0 {
++ reg = <0x0 0x40000>;
++ };
++ uboot@100000 {
++ reg = <0x100000 0x300000>;
++ };
++ data@f00000 {
++ reg = <0xf00000 0x100000>;
++ };
++ };
++ };
++};
++
++&rgb_output {
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0>;
++
++ hdmi_input0:endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&dc_out_dpi0>;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++
++ hdmi_out:endpoint {
++ remote-endpoint = <&tda998x_0_input>;
++ };
++ };
++ };
++};
++
++&spdif {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spdif_pins>;
++};
++
++&spi0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi0_pins>;
++ status = "disabled";
++
++ spi_dev0: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi1_pins>;
++ status = "disabled";
++
++ spi_dev1: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi2_pins>;
++ status = "disabled";
++
++ spi_dev2: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi3_pins>;
++ status = "disabled";
++
++ spi_dev3: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi4 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi4_pins>;
++ status = "disabled";
++
++ spi_dev4: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi5 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi5_pins>;
++ status = "disabled";
++
++ spi_dev5: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&spi6 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi6_pins>;
++ status = "disabled";
++
++ spi_dev6: spi_dev@0 {
++ compatible = "rohm,dh2228fv";
++ reg = <0>;
++ pl022,com-mode = <1>;
++ spi-max-frequency = <10000000>;
++ };
++};
++
++&tda988x_pin {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rgb_pad_pins>;
++ status = "disabled";
++};
++
++&tdm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&tdm_pins>;
++ status = "disabled";
++};
++
++&uart0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart0_pins>;
++ status = "okay";
++};
++
++&uart1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart1_pins>;
++ status = "disabled";
++};
++
++&uart2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart2_pins>;
++ status = "disabled";
++};
++
++&uart3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart3_pins>;
++ status = "disabled";
++};
++
++&uart4 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart4_pins>;
++ status = "disabled";
++};
++
++&uart5 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart5_pins>;
++ status = "disabled";
++};
++
++&usb0 {
++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>,
++ <&stgcrg JH7110_STGCLK_USB0_STB>,
++ <&stgcrg JH7110_STGCLK_USB0_APB>,
++ <&stgcrg JH7110_STGCLK_USB0_AXI>,
++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>,
++ <&stgcrg JH7110_STGCLK_PCIE0_APB>;
++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb", "phy";
++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>,
++ <&stgcrg JH7110_STGRST_USB0_APB>,
++ <&stgcrg JH7110_STGRST_USB0_AXI>,
++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>,
++ <&stgcrg JH7110_STGRST_PCIE0_APB>;
++ reset-names = "pwrup", "apb", "axi", "utmi_apb", "phy";
++ pinctrl-names = "default";
++ pinctrl-0 = <&usb_pins>;
++ dr_mode = "host"; /* host or peripheral */
++ status = "disabled";
++};
++
++&usb_cdns3 {
++ phys = <&usbphy0>, <&pciephy0>;
++ phy-names = "cdns3,usb2-phy", "cdns3,usb3-phy";
++};
++
++&vin_sysctl {
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ /* Parallel bus endpoint */
++ parallel_from_sc2235: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&sc2235_to_parallel>;
++ bus-type = <5>; /* Parallel */
++ bus-width = <8>;
++ data-shift = <2>; /* lines 9:2 are used */
++ hsync-active = <1>;
++ vsync-active = <0>;
++ pclk-sample = <1>;
++ status = "okay";
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ /* CSI2 bus endpoint */
++ csi2rx0_from_ov4689: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&ov4689_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <1 2 3 4>;
++ status = "okay";
++ };
++
++ /* CSI2 bus endpoint */
++ csi2rx0_from_imx219: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&imx219_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <2 1>;
++ lane-polarities = <1 1 1>;
++ status = "okay";
++ };
++
++ csi2rx0_from_imx708: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&imx708_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <2 1>;
++ lane-polarities = <1 1 1>;
++ status = "okay";
++ };
++ };
++ };
++};
++
++&vpu_dec {
++ status = "okay";
++};
++
++&vpu_enc {
++ status = "okay";
++};
++
++&xrp {
++ memory-region = <&xrp_reserved>;
++ status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
+@@ -196,11 +196,60 @@
+ opp-750000000 {
+ opp-hz = /bits/ 64 <750000000>;
+ opp-microvolt = <800000>;
++ opp-suspend;
+ };
+ opp-1500000000 {
+ opp-hz = /bits/ 64 <1500000000>;
+ opp-microvolt = <1040000>;
+ };
++ /* CPU opp table for 1.25GHz */
++ opp-312500000 {
++ opp-hz = /bits/ 64 <312500000>;
++ opp-microvolt = <800000>;
++ };
++ opp-417000000 {
++ opp-hz = /bits/ 64 <417000000>;
++ opp-microvolt = <800000>;
++ };
++ opp-625000000 {
++ opp-hz = /bits/ 64 <625000000>;
++ opp-microvolt = <800000>;
++ opp-suspend;
++ };
++ opp-1250000000 {
++ opp-hz = /bits/ 64 <1250000000>;
++ opp-microvolt = <1000000>;
++ };
++ };
++
++ display: display-subsystem {
++ compatible = "starfive,jh7110-display","verisilicon,display-subsystem";
++ status = "disabled";
++ };
++
++ dsi_output: dsi-output {
++ compatible = "starfive,jh7110-display-encoder","verisilicon,dsi-encoder";
++ status = "disabled";
++ };
++
++ mailbox_client0: mailbox_client {
++ compatible = "starfive,mailbox-test";
++ mbox-names = "rx", "tx";
++ mboxes = <&mailbox_contrl0 0 1>,<&mailbox_contrl0 1 0>;
++ status = "disabled";
++ };
++
++ rgb_output: rgb-output {
++ compatible = "starfive,jh7110-rgb_output","verisilicon,rgb-encoder";
++ //verisilicon,dss-syscon = <&dssctrl>;
++ //verisilicon,mux-mask = <0x70 0x380>;
++ //verisilicon,mux-val = <0x40 0x280>;
++ status = "disabled";
++ };
++
++ tda988x_pin: tda988x_pin {
++ compatible = "starfive,tda998x_rgb_pin";
++ status = "disabled";
+ };
+
+ thermal-zones {
+@@ -349,7 +398,9 @@
+
+ ccache: cache-controller@2010000 {
+ compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache";
+- reg = <0x0 0x2010000 0x0 0x4000>;
++ reg = <0x0 0x2010000 0x0 0x4000>,
++ <0x0 0x8000000 0x0 0x2000000>,
++ <0x0 0xa000000 0x0 0x2000000>;
+ interrupts = <1>, <3>, <4>, <2>;
+ cache-block-size = <64>;
+ cache-level = <2>;
+@@ -378,7 +429,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>,
+ <&syscrg JH7110_SYSCLK_UART0_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART0_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART0_APB>,
++ <&syscrg JH7110_SYSRST_UART0_CORE>;
+ interrupts = <32>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -391,7 +443,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>,
+ <&syscrg JH7110_SYSCLK_UART1_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART1_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART1_APB>,
++ <&syscrg JH7110_SYSRST_UART1_CORE>;
+ interrupts = <33>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -404,7 +457,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>,
+ <&syscrg JH7110_SYSCLK_UART2_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART2_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART2_APB>,
++ <&syscrg JH7110_SYSRST_UART2_CORE>;
+ interrupts = <34>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -513,6 +567,25 @@
+ status = "disabled";
+ };
+
++ spdif: spdif@100a0000 {
++ compatible = "starfive,jh7110-spdif";
++ reg = <0x0 0x100a0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_SPDIF_APB>,
++ <&syscrg JH7110_SYSCLK_SPDIF_CORE>,
++ <&syscrg JH7110_SYSCLK_AUDIO_ROOT>,
++ <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++ <&mclk_ext>, <&syscrg JH7110_SYSCLK_MCLK>;
++ clock-names = "apb", "core",
++ "audroot", "mclk_inner",
++ "mclk_ext", "mclk";
++ resets = <&syscrg JH7110_SYSRST_SPDIF_APB>;
++ reset-names = "apb";
++ interrupts = <84>;
++ interrupt-names = "tx";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
+ pwmdac: pwmdac@100b0000 {
+ compatible = "starfive,jh7110-pwmdac";
+ reg = <0x0 0x100b0000 0x0 0x1000>;
+@@ -526,6 +599,42 @@
+ status = "disabled";
+ };
+
++ pdm: pdm@100d0000 {
++ compatible = "starfive,jh7110-pdm";
++ reg = <0x0 0x100d0000 0x0 0x1000>;
++ reg-names = "pdm";
++ clocks = <&syscrg JH7110_SYSCLK_PDM_DMIC>,
++ <&syscrg JH7110_SYSCLK_PDM_APB>,
++ <&syscrg JH7110_SYSCLK_MCLK>,
++ <&mclk_ext>;
++ clock-names = "pdm_mclk", "pdm_apb",
++ "clk_mclk", "mclk_ext";
++ resets = <&syscrg JH7110_SYSRST_PDM_DMIC>,
++ <&syscrg JH7110_SYSRST_PDM_APB>;
++ reset-names = "pdm_dmic", "pdm_apb";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ i2srx_mst: i2srx_mst@100e0000 {
++ compatible = "starfive,jh7110-i2srx-master";
++ reg = <0x0 0x100e0000 0x0 0x1000>;
++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
++ <&syscrg JH7110_SYSCLK_I2SRX_APB>,
++ <&syscrg JH7110_SYSCLK_MCLK>,
++ <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++ <&mclk_ext>;
++ clock-names = "i2sclk", "apb", "mclk",
++ "mclk_inner","mclk_ext";
++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
++ dmas = <&dma 24>;
++ dma-names = "rx";
++ starfive,syscon = <&sys_syscon 0x18 0x2 0x34 0x3FC00 0x24400>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
+ i2srx: i2s@100e0000 {
+ compatible = "starfive,jh7110-i2srx";
+ reg = <0x0 0x100e0000 0x0 0x1000>;
+@@ -622,6 +731,26 @@
+ #reset-cells = <1>;
+ };
+
++ xrp: xrp@10230000 {
++ compatible = "cdns,xrp";
++ dma-coherent;
++ reg = <0x0 0x10230000 0x0 0x00010000
++ 0x0 0x10240000 0x0 0x00010000>;
++ clocks = <&stgcrg JH7110_STGCLK_HIFI4_CLK_CORE>;
++ clock-names = "core_clk";
++ resets = <&stgcrg JH7110_STGRST_HIFI4_CORE>,
++ <&stgcrg JH7110_STGRST_HIFI4_AXI>;
++ reset-names = "rst_core","rst_axi";
++ starfive,stg-syscon = <&stg_syscon>;
++ firmware-name = "hifi4_elf";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x40000000 0x0 0x20000000 0x040000
++ 0x69c00000 0x0 0x69c00000 0x03000000>;
++ status = "disabled";
++ dsp@0 {};
++ };
++
+ stg_syscon: syscon@10240000 {
+ compatible = "starfive,jh7110-stg-syscon", "syscon";
+ reg = <0x0 0x10240000 0x0 0x1000>;
+@@ -633,7 +762,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>,
+ <&syscrg JH7110_SYSCLK_UART3_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART3_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART3_APB>,
++ <&syscrg JH7110_SYSRST_UART3_CORE>;
+ interrupts = <45>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -646,7 +776,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>,
+ <&syscrg JH7110_SYSCLK_UART4_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART4_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART4_APB>,
++ <&syscrg JH7110_SYSRST_UART4_CORE>;
+ interrupts = <46>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -659,7 +790,8 @@
+ clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>,
+ <&syscrg JH7110_SYSCLK_UART5_APB>;
+ clock-names = "baudclk", "apb_pclk";
+- resets = <&syscrg JH7110_SYSRST_UART5_APB>;
++ resets = <&syscrg JH7110_SYSRST_UART5_APB>,
++ <&syscrg JH7110_SYSRST_UART5_CORE>;
+ interrupts = <47>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+@@ -919,6 +1051,18 @@
+ "ch2", "ch3";
+ };
+
++ mailbox_contrl0: mailbox@13060000 {
++ compatible = "starfive,mail_box";
++ reg = <0x0 0x13060000 0x0 0x0001000>;
++ clocks = <&syscrg JH7110_SYSCLK_MAILBOX_APB>;
++ clock-names = "clk_apb";
++ resets = <&syscrg JH7110_SYSRST_MAILBOX_APB>;
++ reset-names = "mbx_rre";
++ interrupts = <26 27>;
++ #mbox-cells = <2>;
++ status = "disabled";
++ };
++
+ watchdog@13070000 {
+ compatible = "starfive,jh7110-wdt";
+ reg = <0x0 0x13070000 0x0 0x10000>;
+@@ -929,6 +1073,112 @@
+ <&syscrg JH7110_SYSRST_WDT_CORE>;
+ };
+
++ jpu: jpu@13090000 {
++ compatible = "starfive,jpu";
++ dma-coherent;
++ reg = <0x0 0x13090000 0x0 0x300>;
++ interrupts = <14>;
++ clocks = <&syscrg JH7110_SYSCLK_CODAJ12_AXI>,
++ <&syscrg JH7110_SYSCLK_CODAJ12_CORE>,
++ <&syscrg JH7110_SYSCLK_CODAJ12_APB>,
++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>,
++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>,
++ <&syscrg JH7110_SYSCLK_VDEC_JPG>;
++ clock-names = "axi_clk", "core_clk", "apb_clk",
++ "noc_bus", "main_clk", "dec_clk";
++ resets = <&syscrg JH7110_SYSRST_CODAJ12_AXI>,
++ <&syscrg JH7110_SYSRST_CODAJ12_CORE>,
++ <&syscrg JH7110_SYSRST_CODAJ12_APB>;
++ reset-names = "rst_axi", "rst_core", "rst_apb";
++ power-domains = <&pwrc JH7110_PD_VDEC>;
++ status = "disabled";
++ };
++
++ vpu_dec: vpu_dec@130a0000 {
++ compatible = "starfive,vdec";
++ dma-coherent;
++ reg = <0x0 0x130a0000 0x0 0x10000>;
++ interrupts = <13>;
++ clocks = <&syscrg JH7110_SYSCLK_WAVE511_AXI>,
++ <&syscrg JH7110_SYSCLK_WAVE511_BPU>,
++ <&syscrg JH7110_SYSCLK_WAVE511_VCE>,
++ <&syscrg JH7110_SYSCLK_WAVE511_APB>,
++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>,
++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>;
++ clock-names = "axi_clk", "bpu_clk", "vce_clk",
++ "apb_clk", "noc_bus", "main_clk";
++ resets = <&syscrg JH7110_SYSRST_WAVE511_AXI>,
++ <&syscrg JH7110_SYSRST_WAVE511_BPU>,
++ <&syscrg JH7110_SYSRST_WAVE511_VCE>,
++ <&syscrg JH7110_SYSRST_WAVE511_APB>,
++ <&syscrg JH7110_SYSRST_AXIMEM0_AXI>;
++ reset-names = "rst_axi", "rst_bpu", "rst_vce",
++ "rst_apb", "rst_sram";
++ starfive,vdec_noc_ctrl;
++ power-domains = <&pwrc JH7110_PD_VDEC>;
++ status = "disabled";
++ };
++
++ vpu_enc: vpu_enc@130b0000 {
++ compatible = "starfive,venc";
++ dma-coherent;
++ reg = <0x0 0x130b0000 0x0 0x10000>;
++ interrupts = <15>;
++ clocks = <&syscrg JH7110_SYSCLK_WAVE420L_AXI>,
++ <&syscrg JH7110_SYSCLK_WAVE420L_BPU>,
++ <&syscrg JH7110_SYSCLK_WAVE420L_VCE>,
++ <&syscrg JH7110_SYSCLK_WAVE420L_APB>,
++ <&syscrg JH7110_SYSCLK_NOC_BUS_VENC_AXI>;
++ clock-names = "axi_clk", "bpu_clk", "vce_clk",
++ "apb_clk", "noc_bus";
++ resets = <&syscrg JH7110_SYSRST_WAVE420L_AXI>,
++ <&syscrg JH7110_SYSRST_WAVE420L_BPU>,
++ <&syscrg JH7110_SYSRST_WAVE420L_VCE>,
++ <&syscrg JH7110_SYSRST_WAVE420L_APB>,
++ <&syscrg JH7110_SYSRST_AXIMEM1_AXI>;
++ reset-names = "rst_axi", "rst_bpu", "rst_vce",
++ "rst_apb", "rst_sram";
++ starfive,venc_noc_ctrl;
++ power-domains = <&pwrc JH7110_PD_VENC>;
++ status = "disabled";
++ };
++
++ can0: can@130d0000 {
++ compatible = "starfive,jh7110-can", "ipms,can";
++ reg = <0x0 0x130d0000 0x0 0x1000>;
++ interrupts = <112>;
++ clocks = <&syscrg JH7110_SYSCLK_CAN0_APB>,
++ <&syscrg JH7110_SYSCLK_CAN0_CAN>,
++ <&syscrg JH7110_SYSCLK_CAN0_TIMER>;
++ clock-names = "apb_clk", "core_clk", "timer_clk";
++ resets = <&syscrg JH7110_SYSRST_CAN0_APB>,
++ <&syscrg JH7110_SYSRST_CAN0_CORE>,
++ <&syscrg JH7110_SYSRST_CAN0_TIMER>;
++ reset-names = "rst_apb", "rst_core", "rst_timer";
++ frequency = <40000000>;
++ starfive,sys-syscon = <&sys_syscon 0x10 0x3 0x8>;
++ syscon,can_or_canfd = <0>;
++ status = "disabled";
++ };
++
++ can1: can@130e0000 {
++ compatible = "starfive,jh7110-can", "ipms,can";
++ reg = <0x0 0x130e0000 0x0 0x1000>;
++ interrupts = <113>;
++ clocks = <&syscrg JH7110_SYSCLK_CAN1_APB>,
++ <&syscrg JH7110_SYSCLK_CAN1_CAN>,
++ <&syscrg JH7110_SYSCLK_CAN1_TIMER>;
++ clock-names = "apb_clk", "core_clk", "timer_clk";
++ resets = <&syscrg JH7110_SYSRST_CAN1_APB>,
++ <&syscrg JH7110_SYSRST_CAN1_CORE>,
++ <&syscrg JH7110_SYSRST_CAN1_TIMER>;
++ reset-names = "rst_apb", "rst_core", "rst_timer";
++ frequency = <40000000>;
++ starfive,sys-syscon = <&sys_syscon 0x88 0x12 0x40000>;
++ syscon,can_or_canfd = <0>;
++ status = "disabled";
++ };
++
+ crypto: crypto@16000000 {
+ compatible = "starfive,jh7110-crypto";
+ reg = <0x0 0x16000000 0x0 0x4000>;
+@@ -1119,6 +1369,42 @@
+ #power-domain-cells = <1>;
+ };
+
++ rtc: rtc@17040000 {
++ compatible = "starfive,jh7110-rtc";
++ reg = <0x0 0x17040000 0x0 0x10000>;
++ interrupts = <10>, <11>, <12>;
++ interrupt-names = "rtc_ms_pulse", "rtc_sec_pulse", "rtc";
++ clocks = <&aoncrg JH7110_AONCLK_RTC_APB>,
++ <&aoncrg JH7110_AONCLK_RTC_CAL>;
++ clock-names = "pclk", "cal_clk";
++ resets = <&aoncrg JH7110_AONRST_RTC_32K>,
++ <&aoncrg JH7110_AONRST_RTC_APB>,
++ <&aoncrg JH7110_AONRST_RTC_CAL>;
++ reset-names = "rst_osc", "rst_apb", "rst_cal";
++ rtc,cal-clock-freq = <1000000>;
++ };
++
++ gpu: gpu@18000000 {
++ compatible = "img-gpu";
++ reg = <0x0 0x18000000 0x0 0x100000>,
++ <0x0 0x130C000 0x0 0x10000>;
++ clocks = <&syscrg JH7110_SYSCLK_GPU_CORE>,
++ <&syscrg JH7110_SYSCLK_GPU_APB>,
++ <&syscrg JH7110_SYSCLK_GPU_RTC_TOGGLE>,
++ <&syscrg JH7110_SYSCLK_GPU_CORE_CLK>,
++ <&syscrg JH7110_SYSCLK_GPU_SYS_CLK>,
++ <&syscrg JH7110_SYSCLK_NOC_BUS_GPU_AXI>;
++ clock-names = "clk_bv", "clk_apb", "clk_rtc",
++ "clk_core", "clk_sys", "clk_axi";
++ resets = <&syscrg JH7110_SYSRST_GPU_APB>,
++ <&syscrg JH7110_SYSRST_GPU_DOMA>;
++ reset-names = "rst_apb", "rst_doma";
++ power-domains = <&pwrc JH7110_PD_GPUA>;
++ interrupts = <82>;
++ current-clock = <8000000>;
++ status = "disabled";
++ };
++
+ csi2rx: csi-bridge@19800000 {
+ compatible = "starfive,jh7110-csi2rx";
+ reg = <0x0 0x19800000 0x0 0x10000>;
+@@ -1145,6 +1431,67 @@
+ status = "disabled";
+ };
+
++ vin_sysctl: vin_sysctl@19800000 {
++ compatible = "starfive,jh7110-vin";
++ reg = <0x0 0x19800000 0x0 0x10000>,
++ <0x0 0x19810000 0x0 0x10000>,
++ <0x0 0x19820000 0x0 0x10000>,
++ <0x0 0x19840000 0x0 0x10000>,
++ <0x0 0x19870000 0x0 0x30000>,
++ <0x0 0x11840000 0x0 0x10000>,
++ <0x0 0x17030000 0x0 0x10000>,
++ <0x0 0x13020000 0x0 0x10000>;
++ reg-names = "csi2rx", "vclk", "vrst", "sctrl",
++ "isp", "trst", "pmu", "syscrg";
++ clocks = <&ispcrg JH7110_ISPCLK_DOM4_APB_FUNC>,
++ <&ispcrg JH7110_ISPCLK_VIN_APB>,
++ <&ispcrg JH7110_ISPCLK_VIN_SYS>,
++ <&ispcrg JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C>,
++ <&ispcrg JH7110_ISPCLK_DVP_INV>,
++ <&ispcrg JH7110_ISPCLK_VIN_P_AXI_WR>,
++ <&ispcrg JH7110_ISPCLK_MIPI_RX0_PXL>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>,
++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>,
++ <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>,
++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>,
++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>,
++ <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>,
++ <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>;
++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk",
++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr",
++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0",
++ "clk_pixel_clk_if1", "clk_pixel_clk_if2",
++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in",
++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0",
++ "clk_ispcore_2x", "clk_isp_axi";
++ resets = <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_P>,
++ <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_C>,
++ <&ispcrg JH7110_ISPRST_VIN_APB>,
++ <&ispcrg JH7110_ISPRST_VIN_SYS>,
++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_RD>,
++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_WR>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>,
++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>,
++ <&ispcrg JH7110_ISPRST_M31DPHY_HW>,
++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>,
++ <&syscrg JH7110_SYSRST_ISP_TOP>,
++ <&syscrg JH7110_SYSRST_ISP_TOP_AXI>;
++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk",
++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0",
++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3",
++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on",
++ "rst_isp_top_n", "rst_isp_top_axi";
++ starfive,aon-syscon = <&aon_syscon 0x00>;
++ power-domains = <&pwrc JH7110_PD_ISP>;
++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */
++ interrupts = <92 87 88 89 90>;
++ status = "disabled";
++ };
++
+ ispcrg: clock-controller@19810000 {
+ compatible = "starfive,jh7110-ispcrg";
+ reg = <0x0 0x19810000 0x0 0x10000>;
+@@ -1175,6 +1522,66 @@
+ #phy-cells = <0>;
+ };
+
++ dc8200: dc8200@29400000 {
++ compatible = "starfive,jh7110-dc8200","verisilicon,dc8200";
++ verisilicon,dss-syscon = <&dssctrl>;//20220624 panel syscon
++ reg = <0x0 0x29400000 0x0 0x100>,
++ <0x0 0x29400800 0x0 0x2000>,
++ <0x0 0x17030000 0x0 0x1000>;
++ interrupts = <95>;
++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
++ <&syscrg JH7110_SYSCLK_VOUT_SRC>,
++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
++ <&voutcrg JH7110_VOUTCLK_DOM_VOUT_TOP_LCD>,
++ <&hdmitx0_pixelclk>,
++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
++ clock-names = "noc_disp","vout_src",
++ "top_vout_axi","top_vout_ahb",
++ "pix_clk","vout_pix1",
++ "axi_clk","core_clk","vout_ahb",
++ "vout_top_axi","vout_top_lcd","hdmitx0_pixelclk","dc8200_pix0";
++ resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>,
++ <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
++ <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
++ <&voutcrg JH7110_VOUTRST_DC8200_CORE>,
++ <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>;
++ reset-names = "rst_vout_src","rst_axi","rst_ahb","rst_core",
++ "rst_noc_disp";
++ status = "disabled";
++ };
++
++ hdmi: hdmi@29590000 {
++ compatible = "starfive,jh7110-hdmi","inno,hdmi";
++ reg = <0x0 0x29590000 0x0 0x4000>;
++ interrupts = <99>;
++ /*interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;*/
++ /*clocks = <&cru PCLK_HDMI>;*/
++ /*clock-names = "pclk";*/
++ /*pinctrl-names = "default";*/
++ /*pinctrl-0 = <&hdmi_ctl>;*/
++ clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
++ <&hdmitx0_pixelclk>;
++ clock-names = "sysclk", "mclk","bclk","pclk";
++ resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
++ reset-names = "hdmi_tx";
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ dssctrl: dssctrl@295B0000 {
++ compatible = "starfive,jh7110-dssctrl","verisilicon,dss-ctrl", "syscon";
++ reg = <0 0x295B0000 0 0x90>;
++ };
++
+ voutcrg: clock-controller@295c0000 {
+ compatible = "starfive,jh7110-voutcrg";
+ reg = <0x0 0x295c0000 0x0 0x10000>;
+@@ -1193,6 +1600,67 @@
+ power-domains = <&pwrc JH7110_PD_VOUT>;
+ };
+
++ mipi_dsi: mipi@295d0000 {
++ compatible = "starfive,jh7110-mipi_dsi","cdns,dsi";
++ reg = <0x0 0x295d0000 0x0 0x10000>;
++ interrupts = <98>;
++ reg-names = "dsi";
++ clocks = <&voutcrg JH7110_VOUTCLK_DSITX_SYS>,
++ <&voutcrg JH7110_VOUTCLK_DSITX_APB>,
++ <&voutcrg JH7110_VOUTCLK_DSITX_TXESC>,
++ <&voutcrg JH7110_VOUTCLK_DSITX_DPI>;
++ clock-names = "dpi", "apb", "txesc", "sys";
++ resets = <&voutcrg JH7110_VOUTRST_DSITX_DPI>,
++ <&voutcrg JH7110_VOUTRST_DSITX_APB>,
++ <&voutcrg JH7110_VOUTRST_DSITX_RXESC>,
++ <&voutcrg JH7110_VOUTRST_DSITX_SYS>,
++ <&voutcrg JH7110_VOUTRST_DSITX_TXBYTEHS>,
++ <&voutcrg JH7110_VOUTRST_DSITX_TXESC>;
++ reset-names = "dsi_dpi", "dsi_apb", "dsi_rxesc",
++ "dsi_sys", "dsi_txbytehs", "dsi_txesc";
++ phys = <&mipi_dphy>;
++ phy-names = "dphy";
++ status = "disabled";
++ };
++
++ mipi_dphy: mipi-dphy@295e0000{
++ compatible = "starfive,jh7110-mipi-dphy-tx","m31,mipi-dphy-tx";
++ reg = <0x0 0x295e0000 0x0 0x10000>;
++ clocks = <&voutcrg JH7110_VOUTCLK_MIPITX_DPHY_TXESC>;
++ clock-names = "dphy_txesc";
++ resets = <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_SYS>,
++ <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS>;
++ reset-names = "dphy_sys", "dphy_txbytehs";
++ #phy-cells = <0>;
++ status = "disabled";
++ };
++
++ co_process: e24@6e210000 {
++ compatible = "starfive,e24";
++ dma-coherent;
++ reg = <0x0 0x6e210000 0x0 0x00001000>,
++ <0x0 0x6e211000 0x0 0x0003f000>;
++ reg-names = "ecmd", "espace";
++ clocks = <&stgcrg JH7110_STGCLK_E2_RTC>,
++ <&stgcrg JH7110_STGCLK_E2_CORE>,
++ <&stgcrg JH7110_STGCLK_E2_DBG>;
++ clock-names = "clk_rtc", "clk_core", "clk_dbg";
++ resets = <&stgcrg JH7110_STGRST_E24_CORE>;
++ reset-names = "e24_core";
++ starfive,stg-syscon = <&stg_syscon>;
++ interrupt-parent = <&plic>;
++ firmware-name = "e24_elf";
++ irq-mode = <1>;
++ mbox-names = "tx", "rx";
++ mboxes = <&mailbox_contrl0 0 2>,
++ <&mailbox_contrl0 2 0>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x6ce00000 0x0 0x6ce00000 0x1600000>;
++ status = "disabled";
++ dsp@0 {};
++ };
++
+ pcie0: pcie@940000000 {
+ compatible = "starfive,jh7110-pcie";
+ reg = <0x9 0x40000000 0x0 0x1000000>,
--- /dev/null
+From cae7550054ca0cd940bbc1501ae5611f5d2957e6 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 20 Sep 2023 14:53:22 +0800
+Subject: [PATCH 057/116] riscv: dts: starfive: Add JH7110 EVB expanded device
+ tree
+
+Add JH7110 EVB expanded device tree.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile | 11 +-
+ .../starfive/jh7110-evb-can-pdm-pwmdac.dts | 102 ++++++++++++++++
+ .../dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts | 37 ++++++
+ .../dts/starfive/jh7110-evb-i2s-ac108.dts | 72 ++++++++++++
+ .../dts/starfive/jh7110-evb-pcie-i2s-sd.dts | 111 ++++++++++++++++++
+ .../dts/starfive/jh7110-evb-spi-uart2.dts | 65 ++++++++++
+ .../starfive/jh7110-evb-uart1-rgb2hdmi.dts | 57 +++++++++
+ .../starfive/jh7110-evb-uart4-emmc-spdif.dts | 78 ++++++++++++
+ .../starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts | 95 +++++++++++++++
+ .../dts/starfive/jh7110-evb-usbdevice.dts | 35 ++++++
+ 10 files changed, 662 insertions(+), 1 deletion(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -12,4 +12,13 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
+
+-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \
++ jh7110-evb-pcie-i2s-sd.dtb \
++ jh7110-evb-spi-uart2.dtb \
++ jh7110-evb-uart4-emmc-spdif.dtb \
++ jh7110-evb-uart5-pwm-i2c-tdm.dtb \
++ jh7110-evb-dvp-rgb2hdmi.dtb \
++ jh7110-evb-can-pdm-pwmdac.dtb \
++ jh7110-evb-i2s-ac108.dtb \
++ jh7110-evb-usbdevice.dtb \
++ jh7110-evb-uart1-rgb2hdmi.dtb
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts
+@@ -0,0 +1,102 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++ sound2: snd-card2 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-PDM-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "i2s";
++ bitclock-master = <&dailink_master>;
++ frame-master = <&dailink_master>;
++
++ dailink_master:cpu {
++ sound-dai = <&i2srx_mst>;
++ };
++
++ dailink_slave:codec {
++ sound-dai = <&pdm>;
++ };
++ };
++ };
++
++ pwmdac_codec: pwmdac-codec {
++ compatible = "linux,spdif-dit";
++ #sound-dai-cells = <0>;
++ };
++
++ sound3: snd-card3 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "left_j";
++ bitclock-master = <&sndcpu0>;
++ frame-master = <&sndcpu0>;
++
++ sndcpu0: cpu {
++ sound-dai = <&pwmdac>;
++ };
++
++ codec {
++ sound-dai = <&pwmdac_codec>;
++ };
++ };
++ };
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&usb0 {
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&can0 {
++ status = "okay";
++};
++
++&can1 {
++ status = "okay";
++};
++
++&i2srx_mst {
++ status = "okay";
++};
++
++&pwmdac {
++ status = "okay";
++};
++
++&pdm {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts
+@@ -0,0 +1,37 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&vin_sysctl {
++ pinctrl-names = "default";
++ pinctrl-0 = <&dvp_pins>;
++};
++
++&rgb_output {
++ status = "okay";
++};
++
++&tda988x_pin {
++ status = "okay";
++};
++
++&dsi_output {
++ status = "disabled";
++};
++
++&mipi_dsi {
++ status = "disabled";
++};
++
++&mipi_dphy {
++ status = "disabled";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts
+@@ -0,0 +1,72 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++ /* i2s + ac108 */
++ sound0: snd-card0 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-AC108-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "i2s";
++ bitclock-master = <&sndcodec1>;
++ frame-master = <&sndcodec1>;
++
++ widgets = "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ routing = "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++
++ cpu {
++ sound-dai = <&i2srx>;
++ };
++
++ sndcodec1: codec {
++ sound-dai = <&ac108>;
++ clocks = <&ac108_mclk>;
++ clock-names = "mclk";
++ };
++ };
++ };
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&i2c0 {
++ status = "okay";
++};
++
++&i2srx {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts
+@@ -0,0 +1,111 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++ /* i2s + wm8960 */
++ sound6: snd-card6 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-WM8960-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ status = "okay";
++ format = "i2s";
++ bitclock-master = <&sndcodec1>;
++ frame-master = <&sndcodec1>;
++
++ widgets = "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ routing = "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++ cpu0 {
++ sound-dai = <&i2srx>;
++ };
++ cpu1 {
++ sound-dai = <&i2stx1>;
++ };
++
++ sndcodec1:codec {
++ sound-dai = <&wm8960>;
++ clocks = <&wm8960_mclk>;
++ clock-names = "mclk";
++ };
++ };
++ };
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&pcie0 {
++ status = "okay";
++};
++
++&uart3 {
++ status = "okay";
++};
++
++&i2c0 {
++ status = "okay";
++};
++
++&usb0 {
++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>,
++ <&stgcrg JH7110_STGCLK_USB0_STB>,
++ <&stgcrg JH7110_STGCLK_USB0_APB>,
++ <&stgcrg JH7110_STGCLK_USB0_AXI>,
++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>;
++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb";
++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>,
++ <&stgcrg JH7110_STGRST_USB0_APB>,
++ <&stgcrg JH7110_STGRST_USB0_AXI>,
++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>;
++ reset-names = "pwrup", "apb", "axi", "utmi_apb";
++ dr_mode = "host"; /*host or peripheral*/
++ starfive,usb2-only;
++ pinctrl-names = "default";
++ pinctrl-0 = <&usb_pins>;
++ status = "okay";
++};
++
++&i2srx {
++ status = "okay";
++};
++
++&i2stx1 {
++ status = "okay";
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts
+@@ -0,0 +1,65 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&usb0 {
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&uart2 {
++ status = "okay";
++};
++
++&spi0 {
++ status = "okay";
++};
++
++&spi1 {
++ status = "okay";
++};
++
++&spi2 {
++ status = "okay";
++};
++
++&spi3 {
++ status = "okay";
++};
++
++&spi4 {
++ status = "okay";
++};
++
++&spi5 {
++ status = "okay";
++};
++
++&spi6 {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts
+@@ -0,0 +1,57 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&usb0 {
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&uart1 {
++ status = "okay";
++};
++
++&rgb_output {
++ status = "okay";
++};
++
++&tda988x_pin {
++ status = "okay";
++};
++
++&dsi_output {
++ status = "disabled";
++};
++
++&mipi_dsi {
++ status = "disabled";
++};
++
++&mipi_dphy {
++ status = "disabled";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts
+@@ -0,0 +1,78 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++ spdif_transmitter: spdif_transmitter {
++ compatible = "linux,spdif-dit";
++ #sound-dai-cells = <0>;
++ };
++
++ sound4: snd-card4 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-SPDIF-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "left_j";
++ bitclock-master = <&sndcpu0>;
++ frame-master = <&sndcpu0>;
++
++ sndcpu0: cpu {
++ sound-dai = <&spdif>;
++ };
++
++ codec {
++ sound-dai = <&spdif_transmitter>;
++ };
++ };
++ };
++};
++
++&usb0 {
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&uart4 {
++ status = "okay";
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&emmc0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <8>;
++ cap-mmc-highspeed;
++ mmc-hs200-1_8v;
++ non-removable;
++ cap-mmc-hw-reset;
++ board-is-evb;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_ch6to7_pins>;
++ status = "okay";
++};
++
++&spdif {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts
+@@ -0,0 +1,95 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++ sound5: snd-card5 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-TDM-Sound-Card";
++ simple-audio-card,widgets = "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ simple-audio-card,routing = "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "dsp_a";
++ bitclock-master = <&dailink_master>;
++ frame-master = <&dailink_master>;
++
++ cpu {
++ sound-dai = <&tdm>;
++ };
++ dailink_master: codec {
++ sound-dai = <&wm8960>;
++ clocks = <&wm8960_mclk>;
++ };
++ };
++ };
++};
++
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&usb0 {
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
++
++&uart5 {
++ status = "okay";
++};
++
++&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_ch0to3_pins &pwm_ch4to5_pins>;
++ status = "okay";
++};
++
++&tdm {
++ status = "okay";
++};
++
++&i2c0 {
++ status = "okay";
++};
++
++&i2c1 {
++ status = "okay";
++};
++
++&i2c3 {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++ model = "StarFive JH7110 EVB";
++ compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++/* default sd card */
++&mmc0 {
++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++ assigned-clock-rates = <50000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdcard0_pins>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ broken-cd;
++ post-power-on-delay-ms = <200>;
++ status = "okay";
++};
++
++&usb0 {
++ dr_mode = "peripheral"; /*host or peripheral*/
++ status = "okay";
++};
++
++&pcie1 {
++ status = "okay";
++};
--- /dev/null
+From e9122ceaf2d8767753e2a126c14b29b78280446d Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 19 Sep 2023 21:35:39 +0800
+Subject: [PATCH 058/116] riscv: dts: starfive: Add evb-overlay dtso subdir
+
+Create subdir evb-overlay/ and add overlay .dtso for JH7110 EVB.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile | 1 +
+ .../boot/dts/starfive/evb-overlay/Makefile | 7 +
+ .../evb-overlay/jh7110-evb-overlay-can.dtso | 24 ++++
+ .../jh7110-evb-overlay-rgb2hdmi.dtso | 24 ++++
+ .../evb-overlay/jh7110-evb-overlay-sdio.dtso | 78 +++++++++++
+ .../evb-overlay/jh7110-evb-overlay-spi.dtso | 72 ++++++++++
+ .../jh7110-evb-overlay-uart4-emmc.dtso | 130 ++++++++++++++++++
+ .../jh7110-evb-overlay-uart5-pwm.dtso | 92 +++++++++++++
+ 8 files changed, 428 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/Makefile
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
+
++subdir-y += evb-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \
+ jh7110-evb-pcie-i2s-sd.dtb \
+ jh7110-evb-spi-uart2.dtb \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb-overlay-can.dtbo \
++ jh7110-evb-overlay-sdio.dtbo \
++ jh7110-evb-overlay-spi.dtbo \
++ jh7110-evb-overlay-uart4-emmc.dtbo \
++ jh7110-evb-overlay-uart5-pwm.dtbo \
++ jh7110-evb-overlay-rgb2hdmi.dtbo
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //can0
++ fragment@0 {
++ target-path = "/soc/can@130d0000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //can1
++ fragment@1 {
++ target-path = "/soc/can@130e0000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //hdmi_output
++ fragment@0 {
++ target-path = "/tda988x_pin";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //uart1
++ fragment@1 {
++ target-path = "/soc/serial@10010000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso
+@@ -0,0 +1,78 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //sysgpio
++ fragment@0 {
++ target-path = "/soc/pinctrl@13040000";
++ __overlay__ {
++ dt_sdcard1_pins: dt-sdcard1-0 {
++ sdcard-pins {
++ pinmux = <GPIOMUX(56, GPOUT_SYS_SDIO1_CLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(50, GPOUT_SYS_SDIO1_CMD,
++ GPOEN_SYS_SDIO1_CMD,
++ GPI_SYS_SDIO1_CMD)>,
++ <GPIOMUX(49, GPOUT_SYS_SDIO1_DATA0,
++ GPOEN_SYS_SDIO1_DATA0,
++ GPI_SYS_SDIO1_DATA0)>,
++ <GPIOMUX(45, GPOUT_SYS_SDIO1_DATA1,
++ GPOEN_SYS_SDIO1_DATA1,
++ GPI_SYS_SDIO1_DATA1)>,
++ <GPIOMUX(62, GPOUT_SYS_SDIO1_DATA2,
++ GPOEN_SYS_SDIO1_DATA2,
++ GPI_SYS_SDIO1_DATA2)>,
++ <GPIOMUX(40, GPOUT_SYS_SDIO1_DATA3,
++ GPOEN_SYS_SDIO1_DATA3,
++ GPI_SYS_SDIO1_DATA3)>;
++ bias-pull-up;
++ input-enable;
++ };
++ };
++ };
++ };
++
++ //uart3
++ fragment@1 {
++ target-path = "/soc/serial@12000000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //i2c0
++ fragment@2 {
++ target-path = "/soc/i2c@10030000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //mmc1
++ fragment@3 {
++ target-path = "/soc/mmc@16020000";
++ __overlay__ {
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <4>;
++ no-sdio;
++ no-mmc;
++ broken-cd;
++ sd-uhs-sdr12;
++ sd-uhs-sdr25;
++ sd-uhs-sdr50;
++ sd-uhs-sdr104;
++ sd-uhs-ddr50;
++ cap-sd-highspeed;
++ post-power-on-delay-ms = <200>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_sdcard1_pins>;
++ status = "okay";
++ };
++ };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+@@ -0,0 +1,72 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //spi0
++ fragment@0 {
++ target-path = "/soc/spi@10060000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi1
++ fragment@1 {
++ target-path = "/soc/spi@10070000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi2
++ fragment@2 {
++ target-path = "/soc/spi@10080000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi3
++ fragment@3 {
++ target-path = "/soc/spi@12070000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi4
++ fragment@4 {
++ target-path = "/soc/spi@12080000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi5
++ fragment@5 {
++ target-path = "/soc/spi@12090000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //spi6
++ fragment@6 {
++ target-path = "/soc/spi@120a0000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //uart2
++ fragment@7 {
++ target-path = "/soc/serial@10020000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso
+@@ -0,0 +1,130 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //sysgpio
++ fragment@0 {
++ target-path = "/soc/pinctrl@13040000";
++ __overlay__ {
++ dt_emmc0_pins: dt-emmc0-0 {
++ emmc-pins {
++ pinmux = <GPIOMUX(22, GPOUT_SYS_SDIO0_RST,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <PINMUX(64, 0)>,
++ <PINMUX(65, 0)>,
++ <PINMUX(66, 0)>,
++ <PINMUX(67, 0)>,
++ <PINMUX(68, 0)>,
++ <PINMUX(69, 0)>,
++ <PINMUX(70, 0)>,
++ <PINMUX(71, 0)>,
++ <PINMUX(72, 0)>,
++ <PINMUX(73, 0)>;
++ bias-pull-up;
++ drive-strength = <12>;
++ input-enable;
++ slew-rate = <1>;
++ };
++ };
++
++ dt_emmc1_pins: dt-emmc1-0 {
++ emmc-pins {
++ pinmux = <GPIOMUX(51, GPOUT_SYS_SDIO1_RST,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(38, GPOUT_SYS_SDIO1_CLK,
++ GPOEN_ENABLE,
++ GPI_NONE)>,
++ <GPIOMUX(36, GPOUT_SYS_SDIO1_CMD,
++ GPOEN_SYS_SDIO1_CMD,
++ GPI_SYS_SDIO1_CMD)>,
++ <GPIOMUX(43, GPOUT_SYS_SDIO1_DATA0,
++ GPOEN_SYS_SDIO1_DATA0,
++ GPI_SYS_SDIO1_DATA0)>,
++ <GPIOMUX(48, GPOUT_SYS_SDIO1_DATA1,
++ GPOEN_SYS_SDIO1_DATA1,
++ GPI_SYS_SDIO1_DATA1)>,
++ <GPIOMUX(53, GPOUT_SYS_SDIO1_DATA2,
++ GPOEN_SYS_SDIO1_DATA2,
++ GPI_SYS_SDIO1_DATA2)>,
++ <GPIOMUX(63, GPOUT_SYS_SDIO1_DATA3,
++ GPOEN_SYS_SDIO1_DATA3,
++ GPI_SYS_SDIO1_DATA3)>,
++ <GPIOMUX(52, GPOUT_SYS_SDIO1_DATA4,
++ GPOEN_SYS_SDIO1_DATA4,
++ GPI_SYS_SDIO1_DATA4)>,
++ <GPIOMUX(39, GPOUT_SYS_SDIO1_DATA5,
++ GPOEN_SYS_SDIO1_DATA5,
++ GPI_SYS_SDIO1_DATA5)>,
++ <GPIOMUX(46, GPOUT_SYS_SDIO1_DATA6,
++ GPOEN_SYS_SDIO1_DATA6,
++ GPI_SYS_SDIO1_DATA6)>,
++ <GPIOMUX(47, GPOUT_SYS_SDIO1_DATA7,
++ GPOEN_SYS_SDIO1_DATA7,
++ GPI_SYS_SDIO1_DATA7)>;
++ bias-pull-up;
++ input-enable;
++ };
++ };
++ };
++ };
++
++ //aongpio
++ fragment@1 {
++ target-path = "/soc/pinctrl@17020000";
++ __overlay__ {
++ dt_pwm_ch6to7_pins: dt-pwm-ch6to7-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM6, /* PAD_RGPIO0 */
++ GPOEN_AON_PTC0_OE_N_6,
++ GPI_NONE)>,
++ <GPIOMUX(2, GPOUT_AON_PTC0_PWM7, /* PAD_RGPIO1 */
++ GPOEN_AON_PTC0_OE_N_7,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++ };
++ };
++
++ //uart4
++ fragment@2 {
++ target-path = "/soc/serial@12010000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //mmc1
++ fragment@3 {
++ target-path = "/soc/mmc@16020000";
++ __overlay__ {
++ clock-frequency = <102400000>;
++ max-frequency = <100000000>;
++ card-detect-delay = <300>;
++ bus-width = <8>;
++ cap-mmc-hw-reset;
++ non-removable;
++ cap-mmc-highspeed;
++ post-power-on-delay-ms = <200>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_emmc1_pins>;
++ status = "okay";
++ };
++ };
++
++ //ptc
++ fragment@4 {
++ target-path = "/soc/pwm@120d0000";
++ __overlay__ {
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_pwm_ch6to7_pins>;
++ status = "okay";
++ };
++ };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso
+@@ -0,0 +1,92 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //sysgpio
++ fragment@0 {
++ target-path = "/soc/pinctrl@13040000";
++ __overlay__ {
++ dt_pwm_ch0to3_pins: dt-pwm-ch0to3-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(45, GPOUT_SYS_PWM_CHANNEL0,
++ GPOEN_SYS_PWM0_CHANNEL0,
++ GPI_NONE)>,
++ <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL1,
++ GPOEN_SYS_PWM0_CHANNEL1,
++ GPI_NONE)>,
++ <GPIOMUX(47, GPOUT_SYS_PWM_CHANNEL2,
++ GPOEN_SYS_PWM0_CHANNEL2,
++ GPI_NONE)>,
++ <GPIOMUX(48, GPOUT_SYS_PWM_CHANNEL3,
++ GPOEN_SYS_PWM0_CHANNEL3,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++ };
++ };
++
++ //aongpio
++ fragment@1 {
++ target-path = "/soc/pinctrl@17020000";
++ __overlay__ {
++ dt_pwm_ch4to5_pins: dt-pwm-ch4to5-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM4, /* PAD_RGPIO0 */
++ GPOEN_AON_PTC0_OE_N_4,
++ GPI_NONE)>,
++ <GPIOMUX(2, GPOUT_AON_PTC0_PWM5, /* PAD_RGPIO1 */
++ GPOEN_AON_PTC0_OE_N_5,
++ GPI_NONE)>;
++ drive-strength = <12>;
++ };
++ };
++ };
++ };
++
++ //uart5
++ fragment@2 {
++ target-path = "/soc/serial@12020000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //ptc
++ fragment@3 {
++ target-path = "/soc/pwm@120d0000";
++ __overlay__ {
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_pwm_ch0to3_pins &dt_pwm_ch4to5_pins>;
++ status = "okay";
++ };
++ };
++
++ //i2c0
++ fragment@4 {
++ target-path = "/soc/i2c@10030000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //i2c1
++ fragment@5 {
++ target-path = "/soc/i2c@10040000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //i2c3
++ fragment@6 {
++ target-path = "/soc/i2c@12030000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++};
++
--- /dev/null
+From 5c888fa081caf5d9473e733931d1c7b3d4b61e61 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 28 Jul 2023 18:42:55 +0800
+Subject: [PATCH 059/116] riscv: configs: Add starfive_jh7110_defconfig
+
+Add starfive_jh7110_defconfig for JH7110 EVB.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/configs/starfive_jh7110_defconfig | 368 +++++++++++++++++++
+ 1 file changed, 368 insertions(+)
+ create mode 100644 arch/riscv/configs/starfive_jh7110_defconfig
+
+--- /dev/null
++++ b/arch/riscv/configs/starfive_jh7110_defconfig
+@@ -0,0 +1,368 @@
++CONFIG_COMPILE_TEST=y
++# CONFIG_WERROR is not set
++CONFIG_DEFAULT_HOSTNAME="StarFive"
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_USELIB=y
++CONFIG_NO_HZ_IDLE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_BPF_SYSCALL=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_CGROUPS=y
++CONFIG_MEMCG=y
++CONFIG_CGROUP_SCHED=y
++CONFIG_CFS_BANDWIDTH=y
++CONFIG_RT_GROUP_SCHED=y
++CONFIG_CGROUP_PIDS=y
++CONFIG_CGROUP_FREEZER=y
++CONFIG_CGROUP_HUGETLB=y
++CONFIG_CPUSETS=y
++CONFIG_CGROUP_DEVICE=y
++CONFIG_CGROUP_CPUACCT=y
++CONFIG_CGROUP_PERF=y
++CONFIG_CGROUP_BPF=y
++CONFIG_NAMESPACES=y
++CONFIG_USER_NS=y
++CONFIG_CHECKPOINT_RESTORE=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_EXPERT=y
++# CONFIG_SYSFS_SYSCALL is not set
++CONFIG_PROFILING=y
++CONFIG_SOC_MICROCHIP_POLARFIRE=y
++CONFIG_SOC_STARFIVE=y
++CONFIG_SOC_VIRT=y
++CONFIG_ERRATA_SIFIVE=y
++CONFIG_NONPORTABLE=y
++CONFIG_SMP=y
++CONFIG_RISCV_SBI_V01=y
++# CONFIG_RISCV_BOOT_SPINWAIT is not set
++CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation"
++CONFIG_PM_DEBUG=y
++CONFIG_PM_ADVANCED_DEBUG=y
++CONFIG_PM_TEST_SUSPEND=y
++CONFIG_ENERGY_MODEL=y
++CONFIG_CPU_IDLE=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
++CONFIG_CPUFREQ_DT=y
++CONFIG_VIRTUALIZATION=y
++CONFIG_KVM=m
++CONFIG_JUMP_LABEL=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_BINFMT_MISC=y
++CONFIG_CMA=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_XFRM_USER=m
++CONFIG_IP_MULTICAST=y
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_IP_MULTIPLE_TABLES=y
++CONFIG_IP_PNP=y
++CONFIG_IP_PNP_DHCP=y
++CONFIG_IP_PNP_BOOTP=y
++CONFIG_IP_PNP_RARP=y
++CONFIG_INET_ESP=m
++CONFIG_NETFILTER=y
++CONFIG_BRIDGE_NETFILTER=m
++CONFIG_NF_CONNTRACK=m
++CONFIG_NF_CONNTRACK_FTP=m
++CONFIG_NF_CONNTRACK_TFTP=m
++CONFIG_NETFILTER_XT_MARK=m
++CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
++CONFIG_NETFILTER_XT_MATCH_IPVS=m
++CONFIG_IP_VS=m
++CONFIG_IP_VS_PROTO_TCP=y
++CONFIG_IP_VS_PROTO_UDP=y
++CONFIG_IP_VS_RR=m
++CONFIG_IP_VS_NFCT=y
++CONFIG_NF_LOG_ARP=m
++CONFIG_NF_LOG_IPV4=m
++CONFIG_IP_NF_IPTABLES=m
++CONFIG_IP_NF_FILTER=m
++CONFIG_IP_NF_TARGET_REJECT=m
++CONFIG_IP_NF_NAT=m
++CONFIG_IP_NF_TARGET_MASQUERADE=m
++CONFIG_IP_NF_TARGET_REDIRECT=m
++CONFIG_IP_NF_MANGLE=m
++CONFIG_NF_LOG_IPV6=m
++CONFIG_IP6_NF_IPTABLES=m
++CONFIG_IP6_NF_MATCH_IPV6HEADER=m
++CONFIG_IP6_NF_FILTER=m
++CONFIG_IP6_NF_TARGET_REJECT=m
++CONFIG_IP6_NF_MANGLE=m
++CONFIG_BRIDGE=m
++CONFIG_BRIDGE_VLAN_FILTERING=y
++CONFIG_VLAN_8021Q=m
++CONFIG_NET_SCHED=y
++CONFIG_NET_CLS_CGROUP=m
++CONFIG_NETLINK_DIAG=y
++CONFIG_CGROUP_NET_PRIO=y
++CONFIG_CAN=y
++CONFIG_BT=y
++CONFIG_BT_RFCOMM=y
++CONFIG_BT_RFCOMM_TTY=y
++CONFIG_BT_BNEP=y
++CONFIG_BT_BNEP_MC_FILTER=y
++CONFIG_BT_BNEP_PROTO_FILTER=y
++CONFIG_BT_HCIUART=y
++CONFIG_BT_HCIUART_H4=y
++CONFIG_CFG80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_NET_9P=y
++CONFIG_NET_9P_VIRTIO=y
++CONFIG_PCI=y
++CONFIG_PCIEPORTBUS=y
++# CONFIG_PCIEASPM is not set
++CONFIG_PCI_HOST_GENERIC=y
++CONFIG_PCIE_FU740=y
++CONFIG_PCIE_STARFIVE_HOST=y
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_SPI_NOR=y
++CONFIG_OF_CONFIGFS=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_VIRTIO_BLK=y
++CONFIG_BLK_DEV_NVME=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_BLK_DEV_SR=y
++CONFIG_SCSI_VIRTIO=y
++CONFIG_ATA=y
++CONFIG_SATA_AHCI=y
++CONFIG_SATA_AHCI_PLATFORM=y
++CONFIG_MD=y
++CONFIG_BLK_DEV_DM=m
++CONFIG_DM_THIN_PROVISIONING=m
++CONFIG_NETDEVICES=y
++CONFIG_DUMMY=m
++CONFIG_MACVLAN=m
++CONFIG_IPVLAN=m
++CONFIG_VXLAN=m
++CONFIG_VETH=m
++CONFIG_VIRTIO_NET=y
++CONFIG_MACB=y
++CONFIG_E1000E=y
++CONFIG_R8169=y
++CONFIG_STMMAC_ETH=y
++CONFIG_DWMAC_DWC_QOS_ETH=y
++# CONFIG_DWMAC_GENERIC is not set
++CONFIG_DWMAC_STARFIVE=y
++CONFIG_MARVELL_PHY=y
++CONFIG_MICREL_PHY=y
++CONFIG_MICROCHIP_PHY=y
++CONFIG_MICROSEMI_PHY=y
++CONFIG_MOTORCOMM_PHY=y
++CONFIG_IPMS_CAN=y
++CONFIG_IWLWIFI=y
++CONFIG_IWLDVM=y
++CONFIG_IWLMVM=y
++CONFIG_INPUT_MOUSEDEV=y
++CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_TINKER_FT5406=y
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_8250_NR_UARTS=6
++CONFIG_SERIAL_8250_RUNTIME_UARTS=6
++CONFIG_SERIAL_8250_EXTENDED=y
++CONFIG_SERIAL_8250_MANY_PORTS=y
++CONFIG_SERIAL_8250_DW=y
++CONFIG_SERIAL_OF_PLATFORM=y
++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
++CONFIG_TTY_PRINTK=y
++CONFIG_VIRTIO_CONSOLE=y
++CONFIG_HW_RANDOM=y
++CONFIG_HW_RANDOM_VIRTIO=y
++CONFIG_HW_RANDOM_JH7110=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_DESIGNWARE_PLATFORM=y
++CONFIG_SPI=y
++CONFIG_SPI_CADENCE_QUADSPI=y
++CONFIG_SPI_PL022=y
++CONFIG_SPI_SIFIVE=y
++CONFIG_SPI_SPIDEV=y
++# CONFIG_PTP_1588_CLOCK is not set
++CONFIG_GPIO_SYSFS=y
++CONFIG_GPIO_SIFIVE=y
++CONFIG_SENSORS_SFCTEMP=y
++CONFIG_THERMAL=y
++CONFIG_THERMAL_WRITABLE_TRIPS=y
++CONFIG_CPU_THERMAL=y
++CONFIG_THERMAL_EMULATION=y
++CONFIG_WATCHDOG=y
++CONFIG_WATCHDOG_SYSFS=y
++CONFIG_MFD_AXP20X_I2C=y
++CONFIG_REGULATOR=y
++CONFIG_REGULATOR_AXP20X=y
++CONFIG_REGULATOR_STARFIVE_JH7110=y
++# CONFIG_MEDIA_CEC_SUPPORT is not set
++CONFIG_MEDIA_SUPPORT=y
++CONFIG_MEDIA_USB_SUPPORT=y
++CONFIG_USB_VIDEO_CLASS=y
++CONFIG_V4L_PLATFORM_DRIVERS=y
++CONFIG_V4L_MEM2MEM_DRIVERS=y
++CONFIG_VIDEO_WAVE_VPU=m
++CONFIG_VIN_SENSOR_SC2235=y
++CONFIG_VIN_SENSOR_OV4689=y
++CONFIG_VIN_SENSOR_IMX219=y
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIDEO_IMX708=y
++CONFIG_DRM_I2C_NXP_TDA998X=y
++CONFIG_DRM_I2C_NXP_TDA9950=y
++CONFIG_DRM_RADEON=m
++CONFIG_DRM_VIRTIO_GPU=m
++CONFIG_DRM_VERISILICON=y
++CONFIG_STARFIVE_INNO_HDMI=y
++CONFIG_STARFIVE_DSI=y
++CONFIG_DRM_IMG_ROGUE=y
++CONFIG_DRM_LEGACY=y
++CONFIG_FB=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_USB_AUDIO=y
++CONFIG_SND_SOC=y
++CONFIG_SND_DESIGNWARE_I2S=y
++# CONFIG_SND_SOC_INTEL_SST_TOPLEVEL is not set
++CONFIG_SND_SOC_STARFIVE=y
++CONFIG_SND_SOC_JH7110_PDM=y
++CONFIG_SND_SOC_JH7110_PWMDAC=y
++CONFIG_SND_SOC_JH7110_SPDIF=y
++CONFIG_SND_SOC_JH7110_TDM=y
++CONFIG_SND_SOC_AC108=y
++CONFIG_SND_SOC_WM8960=y
++CONFIG_SND_SIMPLE_CARD=y
++CONFIG_USB=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_HCD_PLATFORM=y
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_OHCI_HCD_PLATFORM=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_UAS=y
++CONFIG_USB_CDNS_SUPPORT=y
++CONFIG_USB_CDNS3=y
++CONFIG_USB_CDNS3_GADGET=y
++CONFIG_USB_CDNS3_HOST=y
++CONFIG_USB_CDNS3_STARFIVE=y
++CONFIG_USB_GADGET=y
++CONFIG_USB_CONFIGFS=y
++CONFIG_USB_CONFIGFS_MASS_STORAGE=y
++CONFIG_USB_CONFIGFS_F_FS=y
++CONFIG_MMC=y
++CONFIG_MMC_SDHCI=y
++CONFIG_MMC_SDHCI_PLTFM=y
++CONFIG_MMC_SDHCI_CADENCE=y
++CONFIG_MMC_SPI=y
++CONFIG_MMC_DW=y
++CONFIG_MMC_DW_STARFIVE=y
++CONFIG_RTC_CLASS=y
++# CONFIG_RTC_DRV_SPEAR is not set
++CONFIG_RTC_DRV_STARFIVE=y
++CONFIG_DMADEVICES=y
++CONFIG_AMBA_PL08X=y
++CONFIG_DW_AXI_DMAC=y
++# CONFIG_SH_DMAE_BASE is not set
++# CONFIG_TI_EDMA is not set
++# CONFIG_DMA_OMAP is not set
++CONFIG_DMATEST=y
++CONFIG_VIRTIO_PCI=y
++CONFIG_VIRTIO_BALLOON=y
++CONFIG_VIRTIO_INPUT=y
++CONFIG_VIRTIO_MMIO=y
++CONFIG_STAGING=y
++CONFIG_STAGING_MEDIA=y
++CONFIG_CLK_STARFIVE_JH7110_AON=y
++CONFIG_CLK_STARFIVE_JH7110_STG=y
++CONFIG_CLK_STARFIVE_JH7110_ISP=y
++CONFIG_CLK_STARFIVE_JH7110_VOUT=y
++CONFIG_MAILBOX=y
++CONFIG_STARFIVE_MBOX=m
++CONFIG_STARFIVE_MBOX_TEST=m
++CONFIG_RPMSG_CHAR=y
++CONFIG_RPMSG_CTRL=y
++CONFIG_RPMSG_VIRTIO=y
++CONFIG_SIFIVE_CCACHE=y
++CONFIG_PWM=y
++CONFIG_PWM_OCORES=y
++CONFIG_PHY_STARFIVE_JH7110_PCIE=y
++CONFIG_PHY_STARFIVE_JH7110_USB=y
++CONFIG_PHY_M31_DPHY_RX0=y
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_POSIX_ACL=y
++CONFIG_EXT4_FS_SECURITY=y
++CONFIG_BTRFS_FS=m
++CONFIG_BTRFS_FS_POSIX_ACL=y
++CONFIG_AUTOFS_FS=y
++CONFIG_FUSE_FS=y
++CONFIG_OVERLAY_FS=y
++CONFIG_OVERLAY_FS_INDEX=y
++CONFIG_OVERLAY_FS_XINO_AUTO=y
++CONFIG_OVERLAY_FS_METACOPY=y
++CONFIG_ISO9660_FS=y
++CONFIG_JOLIET=y
++CONFIG_ZISOFS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_EXFAT_FS=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_POSIX_ACL=y
++CONFIG_HUGETLBFS=y
++CONFIG_JFFS2_FS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V4=y
++CONFIG_NFS_V4_1=y
++CONFIG_NFS_V4_2=y
++CONFIG_ROOT_NFS=y
++CONFIG_9P_FS=y
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ISO8859_1=m
++CONFIG_SECURITY=y
++CONFIG_SECURITY_SELINUX=y
++CONFIG_SECURITY_APPARMOR=y
++CONFIG_DEFAULT_SECURITY_DAC=y
++CONFIG_LSM=""
++CONFIG_INIT_STACK_NONE=y
++CONFIG_CRYPTO_USER=y
++CONFIG_CRYPTO_ZSTD=y
++CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_CRYPTO_USER_API_SKCIPHER=y
++CONFIG_CRYPTO_USER_API_AEAD=y
++CONFIG_CRYPTO_STATS=y
++CONFIG_CRYPTO_DEV_VIRTIO=y
++CONFIG_CRYPTO_DEV_JH7110=y
++CONFIG_DMA_CMA=y
++CONFIG_PRINTK_TIME=y
++CONFIG_DEBUG_FS=y
++CONFIG_DEBUG_PAGEALLOC=y
++CONFIG_SCHED_STACK_END_CHECK=y
++CONFIG_DEBUG_VM=y
++CONFIG_DEBUG_VM_PGFLAGS=y
++CONFIG_DEBUG_MEMORY_INIT=y
++CONFIG_DEBUG_PER_CPU_MAPS=y
++CONFIG_SOFTLOCKUP_DETECTOR=y
++CONFIG_WQ_WATCHDOG=y
++CONFIG_DEBUG_TIMEKEEPING=y
++CONFIG_DEBUG_RT_MUTEXES=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_MUTEXES=y
++CONFIG_DEBUG_RWSEMS=y
++CONFIG_DEBUG_LIST=y
++CONFIG_DEBUG_PLIST=y
++CONFIG_DEBUG_SG=y
++CONFIG_RCU_CPU_STALL_TIMEOUT=60
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_EQS_DEBUG=y
++# CONFIG_FTRACE is not set
++# CONFIG_RUNTIME_TESTING_MENU is not set
++CONFIG_MEMTEST=y
--- /dev/null
+From 95c702022f5e4cb786719fcf90170334b1e562cc Mon Sep 17 00:00:00 2001
+From: Jianlong Huang <jianlong.huang@starfivetech.com>
+Date: Thu, 16 Jun 2022 17:13:57 +0800
+Subject: [PATCH 060/116] of: configfs: Add configfs function
+
+Signed-off-by: Jianlong Huang <jianlong.huang@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/of/Kconfig | 7 ++
+ drivers/of/Makefile | 1 +
+ drivers/of/configfs.c | 277 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 285 insertions(+)
+ create mode 100644 drivers/of/configfs.c
+
+--- a/drivers/of/Kconfig
++++ b/drivers/of/Kconfig
+@@ -102,4 +102,11 @@ config OF_OVERLAY
+ config OF_NUMA
+ bool
+
++config OF_CONFIGFS
++ bool "Device Tree Overlay ConfigFS interface"
++ select CONFIGFS_FS
++ select OF_OVERLAY
++ help
++ Enable a simple user-space driven DT overlay interface.
++
+ endif # OF
+--- a/drivers/of/Makefile
++++ b/drivers/of/Makefile
+@@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o
+ obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+ obj-$(CONFIG_OF_RESOLVE) += resolver.o
+ obj-$(CONFIG_OF_OVERLAY) += overlay.o
++obj-$(CONFIG_OF_CONFIGFS) += configfs.o
+ obj-$(CONFIG_OF_NUMA) += of_numa.o
+
+ ifdef CONFIG_KEXEC_FILE
+--- /dev/null
++++ b/drivers/of/configfs.c
+@@ -0,0 +1,277 @@
++/*
++ * Configfs entries for device-tree
++ *
++ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++#include <linux/ctype.h>
++#include <linux/cpu.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_fdt.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/proc_fs.h>
++#include <linux/configfs.h>
++#include <linux/types.h>
++#include <linux/stat.h>
++#include <linux/limits.h>
++#include <linux/file.h>
++#include <linux/vmalloc.h>
++#include <linux/firmware.h>
++#include <linux/sizes.h>
++
++#include "of_private.h"
++
++struct cfs_overlay_item {
++ struct config_item item;
++
++ char path[PATH_MAX];
++
++ const struct firmware *fw;
++ struct device_node *overlay;
++ int ov_id;
++
++ void *dtbo;
++ int dtbo_size;
++};
++
++static inline struct cfs_overlay_item *to_cfs_overlay_item(
++ struct config_item *item)
++{
++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
++}
++
++static ssize_t cfs_overlay_item_path_show(struct config_item *item,
++ char *page)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++ return sprintf(page, "%s\n", overlay->path);
++}
++
++static ssize_t cfs_overlay_item_path_store(struct config_item *item,
++ const char *page, size_t count)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++ const char *p = page;
++ char *s;
++ int err;
++
++ /* if it's set do not allow changes */
++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
++ return -EPERM;
++
++ /* copy to path buffer (and make sure it's always zero terminated */
++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
++ overlay->path[sizeof(overlay->path) - 1] = '\0';
++
++ /* strip trailing newlines */
++ s = overlay->path + strlen(overlay->path);
++ while (s > overlay->path && *--s == '\n')
++ *s = '\0';
++
++ pr_debug("%s: path is '%s'\n", __func__, overlay->path);
++
++ err = request_firmware(&overlay->fw, overlay->path, NULL);
++ if (err != 0)
++ return err;
++
++ err = of_overlay_fdt_apply((void *)overlay->fw->data,
++ (u32)overlay->fw->size, &overlay->ov_id, NULL);
++ if (err != 0)
++ goto out_err;
++
++ return count;
++
++out_err:
++
++ release_firmware(overlay->fw);
++ overlay->fw = NULL;
++
++ overlay->path[0] = '\0';
++ return err;
++}
++
++static ssize_t cfs_overlay_item_status_show(struct config_item *item,
++ char *page)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++ return sprintf(page, "%s\n",
++ overlay->ov_id > 0 ? "applied" : "unapplied");
++}
++
++CONFIGFS_ATTR(cfs_overlay_item_, path);
++CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
++
++static struct configfs_attribute *cfs_overlay_attrs[] = {
++ &cfs_overlay_item_attr_path,
++ &cfs_overlay_item_attr_status,
++ NULL,
++};
++
++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
++ void *buf, size_t max_count)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++ pr_debug("%s: buf=%p max_count=%zu\n", __func__,
++ buf, max_count);
++
++ if (overlay->dtbo == NULL)
++ return 0;
++
++ /* copy if buffer provided */
++ if (buf != NULL) {
++ /* the buffer must be large enough */
++ if (overlay->dtbo_size > max_count)
++ return -ENOSPC;
++
++ memcpy(buf, overlay->dtbo, overlay->dtbo_size);
++ }
++
++ return overlay->dtbo_size;
++}
++
++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
++ const void *buf, size_t count)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++ int err;
++
++ /* if it's set do not allow changes */
++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
++ return -EPERM;
++
++ /* copy the contents */
++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
++ if (overlay->dtbo == NULL)
++ return -ENOMEM;
++
++ overlay->dtbo_size = count;
++
++ err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size,
++ &overlay->ov_id, NULL);
++ if (err != 0)
++ goto out_err;
++
++ return count;
++
++out_err:
++ kfree(overlay->dtbo);
++ overlay->dtbo = NULL;
++ overlay->dtbo_size = 0;
++ overlay->ov_id = 0;
++
++ return err;
++}
++
++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
++
++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
++ &cfs_overlay_item_attr_dtbo,
++ NULL,
++};
++
++static void cfs_overlay_release(struct config_item *item)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++ if (overlay->ov_id > 0)
++ of_overlay_remove(&overlay->ov_id);
++ if (overlay->fw)
++ release_firmware(overlay->fw);
++ /* kfree with NULL is safe */
++ kfree(overlay->dtbo);
++ kfree(overlay);
++}
++
++static struct configfs_item_operations cfs_overlay_item_ops = {
++ .release = cfs_overlay_release,
++};
++
++static struct config_item_type cfs_overlay_type = {
++ .ct_item_ops = &cfs_overlay_item_ops,
++ .ct_attrs = cfs_overlay_attrs,
++ .ct_bin_attrs = cfs_overlay_bin_attrs,
++ .ct_owner = THIS_MODULE,
++};
++
++static struct config_item *cfs_overlay_group_make_item(
++ struct config_group *group, const char *name)
++{
++ struct cfs_overlay_item *overlay;
++
++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
++ if (!overlay)
++ return ERR_PTR(-ENOMEM);
++
++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
++ return &overlay->item;
++}
++
++static void cfs_overlay_group_drop_item(struct config_group *group,
++ struct config_item *item)
++{
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++ config_item_put(&overlay->item);
++}
++
++static struct configfs_group_operations overlays_ops = {
++ .make_item = cfs_overlay_group_make_item,
++ .drop_item = cfs_overlay_group_drop_item,
++};
++
++static struct config_item_type overlays_type = {
++ .ct_group_ops = &overlays_ops,
++ .ct_owner = THIS_MODULE,
++};
++
++static struct configfs_group_operations of_cfs_ops = {
++ /* empty - we don't allow anything to be created */
++};
++
++static struct config_item_type of_cfs_type = {
++ .ct_group_ops = &of_cfs_ops,
++ .ct_owner = THIS_MODULE,
++};
++
++struct config_group of_cfs_overlay_group;
++
++static struct configfs_subsystem of_cfs_subsys = {
++ .su_group = {
++ .cg_item = {
++ .ci_namebuf = "device-tree",
++ .ci_type = &of_cfs_type,
++ },
++ },
++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
++};
++
++static int __init of_cfs_init(void)
++{
++ int ret;
++
++ pr_info("%s\n", __func__);
++
++ config_group_init(&of_cfs_subsys.su_group);
++ config_group_init_type_name(&of_cfs_overlay_group, "overlays",
++ &overlays_type);
++ configfs_add_default_group(&of_cfs_overlay_group,
++ &of_cfs_subsys.su_group);
++
++ ret = configfs_register_subsystem(&of_cfs_subsys);
++ if (ret != 0) {
++ pr_err("%s: failed to register subsys\n", __func__);
++ goto out;
++ }
++ pr_info("%s: OK\n", __func__);
++out:
++ return ret;
++}
++late_initcall(of_cfs_init);
--- /dev/null
+From 7891826f8c2de9ee0f6459cf969f7b082e29b154 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Thu, 1 Jun 2023 23:10:09 -0700
+Subject: [PATCH 061/116] usr: Add gen_initramfs_list.sh
+
+Add gen_initramfs_list.sh
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ usr/gen_initramfs_list.sh | 328 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 328 insertions(+)
+ create mode 100644 usr/gen_initramfs_list.sh
+
+--- /dev/null
++++ b/usr/gen_initramfs_list.sh
+@@ -0,0 +1,328 @@
++#!/bin/sh
++# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
++# Copyright (C) 2006 Sam Ravnborg <sam@ravnborg.org>
++#
++# Released under the terms of the GNU GPL
++#
++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
++# the cpio archive, and then compresses it.
++# The script may also be used to generate the inputfile used for gen_init_cpio
++# This script assumes that gen_init_cpio is located in usr/ directory
++
++# error out on errors
++set -e
++
++usage() {
++cat << EOF
++Usage:
++$0 [-o <file>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
++ -o <file> Create compressed initramfs file named <file> using
++ gen_init_cpio and compressor depending on the extension
++ -u <uid> User ID to map to user ID 0 (root).
++ <uid> is only meaningful if <cpio_source> is a
++ directory. "squash" forces all files to uid 0.
++ -g <gid> Group ID to map to group ID 0 (root).
++ <gid> is only meaningful if <cpio_source> is a
++ directory. "squash" forces all files to gid 0.
++ <cpio_source> File list or directory for cpio archive.
++ If <cpio_source> is a .cpio file it will be used
++ as direct input to initramfs.
++ -d Output the default cpio list.
++
++All options except -o and -l may be repeated and are interpreted
++sequentially and immediately. -u and -g states are preserved across
++<cpio_source> options so an explicit "-u 0 -g 0" is required
++to reset the root/group mapping.
++EOF
++}
++
++# awk style field access
++# $1 - field number; rest is argument string
++field() {
++ shift $1 ; echo $1
++}
++
++list_default_initramfs() {
++ # echo usr/kinit/kinit
++ :
++}
++
++default_initramfs() {
++ cat <<-EOF >> ${output}
++ # This is a very simple, default initramfs
++
++ dir /dev 0755 0 0
++ nod /dev/console 0600 0 0 c 5 1
++ dir /root 0700 0 0
++ # file /kinit usr/kinit/kinit 0755 0 0
++ # slink /init kinit 0755 0 0
++ EOF
++}
++
++filetype() {
++ local argv1="$1"
++
++ # symlink test must come before file test
++ if [ -L "${argv1}" ]; then
++ echo "slink"
++ elif [ -f "${argv1}" ]; then
++ echo "file"
++ elif [ -d "${argv1}" ]; then
++ echo "dir"
++ elif [ -b "${argv1}" -o -c "${argv1}" ]; then
++ echo "nod"
++ elif [ -p "${argv1}" ]; then
++ echo "pipe"
++ elif [ -S "${argv1}" ]; then
++ echo "sock"
++ else
++ echo "invalid"
++ fi
++ return 0
++}
++
++list_print_mtime() {
++ :
++}
++
++print_mtime() {
++ local my_mtime="0"
++
++ if [ -e "$1" ]; then
++ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
++ fi
++
++ echo "# Last modified: ${my_mtime}" >> ${output}
++ echo "" >> ${output}
++}
++
++list_parse() {
++ if [ -L "$1" ]; then
++ return
++ fi
++ echo "$1" | sed 's/:/\\:/g; s/$/ \\/'
++}
++
++# for each file print a line in following format
++# <filetype> <name> <path to file> <octal mode> <uid> <gid>
++# for links, devices etc the format differs. See gen_init_cpio for details
++parse() {
++ local location="$1"
++ local name="/${location#${srcdir}}"
++ # change '//' into '/'
++ name=$(echo "$name" | sed -e 's://*:/:g')
++ local mode="$2"
++ local uid="$3"
++ local gid="$4"
++ local ftype=$(filetype "${location}")
++ # remap uid/gid to 0 if necessary
++ [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0
++ [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0
++ local str="${mode} ${uid} ${gid}"
++
++ [ "${ftype}" = "invalid" ] && return 0
++ [ "${location}" = "${srcdir}" ] && return 0
++
++ case "${ftype}" in
++ "file")
++ str="${ftype} ${name} ${location} ${str}"
++ ;;
++ "nod")
++ local dev=`LC_ALL=C ls -l "${location}"`
++ local maj=`field 5 ${dev}`
++ local min=`field 6 ${dev}`
++ maj=${maj%,}
++
++ [ -b "${location}" ] && dev="b" || dev="c"
++
++ str="${ftype} ${name} ${str} ${dev} ${maj} ${min}"
++ ;;
++ "slink")
++ local target=`readlink "${location}"`
++ str="${ftype} ${name} ${target} ${str}"
++ ;;
++ *)
++ str="${ftype} ${name} ${str}"
++ ;;
++ esac
++
++ echo "${str}" >> ${output}
++
++ return 0
++}
++
++unknown_option() {
++ printf "ERROR: unknown option \"$arg\"\n" >&2
++ printf "If the filename validly begins with '-', " >&2
++ printf "then it must be prefixed\n" >&2
++ printf "by './' so that it won't be interpreted as an option." >&2
++ printf "\n" >&2
++ usage >&2
++ exit 1
++}
++
++list_header() {
++ :
++}
++
++header() {
++ printf "\n#####################\n# $1\n" >> ${output}
++}
++
++# process one directory (incl sub-directories)
++dir_filelist() {
++ ${dep_list}header "$1"
++
++ srcdir=$(echo "$1" | sed -e 's://*:/:g')
++ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LANG=C sort)
++
++ # If $dirlist is only one line, then the directory is empty
++ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
++ ${dep_list}print_mtime "$1"
++
++ echo "${dirlist}" | \
++ while read x; do
++ ${dep_list}parse ${x}
++ done
++ fi
++}
++
++# if only one file is specified and it is .cpio file then use it direct as fs
++# if a directory is specified then add all files in given direcotry to fs
++# if a regular file is specified assume it is in gen_initramfs format
++input_file() {
++ source="$1"
++ if [ -f "$1" ]; then
++ ${dep_list}header "$1"
++ is_cpio="$(echo "$1" | sed 's/^.*\.cpio\(\..*\)\{0,1\}/cpio/')"
++ if [ $2 -eq 0 -a ${is_cpio} = "cpio" ]; then
++ cpio_file=$1
++ echo "$1" | grep -q '^.*\.cpio\..*' && is_cpio_compressed="compressed"
++ [ ! -z ${dep_list} ] && echo "$1"
++ return 0
++ fi
++ if [ -z ${dep_list} ]; then
++ print_mtime "$1" >> ${output}
++ cat "$1" >> ${output}
++ else
++ echo "$1 \\"
++ cat "$1" | while read type dir file perm ; do
++ if [ "$type" = "file" ]; then
++ echo "$file \\";
++ fi
++ done
++ fi
++ elif [ -d "$1" ]; then
++ dir_filelist "$1"
++ else
++ echo " ${prog}: Cannot open '$1'" >&2
++ exit 1
++ fi
++}
++
++prog=$0
++root_uid=0
++root_gid=0
++dep_list=
++cpio_file=
++cpio_list=
++output="/dev/stdout"
++output_file=""
++is_cpio_compressed=
++compr="gzip -n -9 -f"
++
++arg="$1"
++case "$arg" in
++ "-l") # files included in initramfs - used by kbuild
++ dep_list="list_"
++ echo "deps_initramfs := $0 \\"
++ shift
++ ;;
++ "-o") # generate compressed cpio image named $1
++ shift
++ output_file="$1"
++ cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
++ output=${cpio_list}
++ echo "$output_file" | grep -q "\.gz$" \
++ && [ -x "`which gzip 2> /dev/null`" ] \
++ && compr="gzip -n -9 -f"
++ echo "$output_file" | grep -q "\.bz2$" \
++ && [ -x "`which bzip2 2> /dev/null`" ] \
++ && compr="bzip2 -9 -f"
++ echo "$output_file" | grep -q "\.lzma$" \
++ && [ -x "`which lzma 2> /dev/null`" ] \
++ && compr="lzma -9 -f"
++ echo "$output_file" | grep -q "\.xz$" \
++ && [ -x "`which xz 2> /dev/null`" ] \
++ && compr="xz --check=crc32 --lzma2=dict=1MiB"
++ echo "$output_file" | grep -q "\.lzo$" \
++ && [ -x "`which lzop 2> /dev/null`" ] \
++ && compr="lzop -9 -f"
++ echo "$output_file" | grep -q "\.lz4$" \
++ && [ -x "`which lz4 2> /dev/null`" ] \
++ && compr="lz4 -l -9 -f"
++ echo "$output_file" | grep -q "\.cpio$" && compr="cat"
++ shift
++ ;;
++esac
++while [ $# -gt 0 ]; do
++ arg="$1"
++ shift
++ case "$arg" in
++ "-u") # map $1 to uid=0 (root)
++ root_uid="$1"
++ [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0)
++ shift
++ ;;
++ "-g") # map $1 to gid=0 (root)
++ root_gid="$1"
++ [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0)
++ shift
++ ;;
++ "-d") # display default initramfs list
++ default_list="$arg"
++ ${dep_list}default_initramfs
++ ;;
++ "-h")
++ usage
++ exit 0
++ ;;
++ *)
++ case "$arg" in
++ "-"*)
++ unknown_option
++ ;;
++ *) # input file/dir - process it
++ input_file "$arg" "$#"
++ ;;
++ esac
++ ;;
++ esac
++done
++
++# If output_file is set we will generate cpio archive and compress it
++# we are careful to delete tmp files
++if [ ! -z ${output_file} ]; then
++ if [ -z ${cpio_file} ]; then
++ timestamp=
++ if test -n "$KBUILD_BUILD_TIMESTAMP"; then
++ timestamp="$(date -d"$KBUILD_BUILD_TIMESTAMP" +%s || :)"
++ if test -n "$timestamp"; then
++ timestamp="-t $timestamp"
++ fi
++ fi
++ cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
++ usr/gen_init_cpio $timestamp ${cpio_list} > ${cpio_tfile}
++ else
++ cpio_tfile=${cpio_file}
++ fi
++ rm ${cpio_list}
++ if [ "${is_cpio_compressed}" = "compressed" ]; then
++ cat ${cpio_tfile} > ${output_file}
++ else
++ (cat ${cpio_tfile} | ${compr} - > ${output_file}) \
++ || (rm -f ${output_file} ; false)
++ fi
++ [ -z ${cpio_file} ] && rm ${cpio_tfile}
++fi
++exit 0
--- /dev/null
+From dcc2827ed6e701a65731c05b0297745559837217 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 12 May 2023 17:33:20 +0800
+Subject: [PATCH 062/116] i2c: designware: Delete SMBus functionalities
+
+The driver didn't implement the smbus interface,
+so replace the SMBus functionalities with
+I2C_FUNC_SMBUS_EMUL.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/i2c/busses/i2c-designware-core.h | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-designware-core.h
++++ b/drivers/i2c/busses/i2c-designware-core.h
+@@ -18,12 +18,10 @@
+ #include <linux/regmap.h>
+ #include <linux/types.h>
+
+-#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
+- I2C_FUNC_SMBUS_BYTE | \
+- I2C_FUNC_SMBUS_BYTE_DATA | \
+- I2C_FUNC_SMBUS_WORD_DATA | \
+- I2C_FUNC_SMBUS_BLOCK_DATA | \
+- I2C_FUNC_SMBUS_I2C_BLOCK)
++#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL \
++ & ~I2C_FUNC_SMBUS_QUICK \
++ & ~I2C_FUNC_SMBUS_PROC_CALL \
++ & ~I2C_FUNC_SMBUS_PEC))
+
+ #define DW_IC_CON_MASTER BIT(0)
+ #define DW_IC_CON_SPEED_STD (1 << 1)
--- /dev/null
+From b61cefc6c785aa8a7177a0b535db746fd0047bd8 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Fri, 19 Jan 2024 15:22:55 +0800
+Subject: [PATCH 063/116] drivers: mtd: gigadevice: add gd25lq256d 32M flash
+ support
+
+add gd25lq256d 32M flash support
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ drivers/mtd/spi-nor/gigadevice.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -66,6 +66,10 @@ static const struct flash_info gigadevic
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
++ { "gd25lq256d", INFO(0xc86019, 0, 64 * 1024, 512)
++ FLAGS( SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_QUAD_PP)
++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++ SPI_NOR_QUAD_READ) },
+ { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512)
+ PARSE_SFDP
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
--- /dev/null
+From 76bc13aa12bd111f5da01e107f8d487b20b5a40c Mon Sep 17 00:00:00 2001
+From: "shanlong.li" <shanlong.li@starfivetech.com>
+Date: Thu, 8 Jun 2023 00:07:15 -0700
+Subject: [PATCH 064/116] driver: mailbox: Add mailbox driver
+
+Add mailbox driver.
+
+Signed-off-by: shanlong.li <shanlong.li@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/mailbox/Kconfig | 13 +
+ drivers/mailbox/Makefile | 4 +
+ drivers/mailbox/starfive_mailbox-test.c | 407 ++++++++++++++++++++++++
+ drivers/mailbox/starfive_mailbox.c | 347 ++++++++++++++++++++
+ 4 files changed, 771 insertions(+)
+ create mode 100644 drivers/mailbox/starfive_mailbox-test.c
+ create mode 100644 drivers/mailbox/starfive_mailbox.c
+
+--- a/drivers/mailbox/Kconfig
++++ b/drivers/mailbox/Kconfig
+@@ -295,4 +295,17 @@ config QCOM_IPCC
+ acts as an interrupt controller for receiving interrupts from clients.
+ Say Y here if you want to build this driver.
+
++config STARFIVE_MBOX
++ tristate "Platform Starfive Mailbox"
++ depends on OF
++ help
++ Say Y here if you want to build a platform specific variant RISCV
++ controller driver.
++
++config STARFIVE_MBOX_TEST
++ tristate "Starfive Mailbox Test Client"
++ depends on OF
++ depends on HAS_IOMEM
++ help
++ Test client to help with testing new Controller driver implementations.
+ endif
+--- a/drivers/mailbox/Makefile
++++ b/drivers/mailbox/Makefile
+@@ -62,3 +62,7 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox
+ obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
+
+ obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o
++
++obj-$(CONFIG_STARFIVE_MBOX) += starfive_mailbox.o
++
++obj-$(CONFIG_STARFIVE_MBOX_TEST) += starfive_mailbox-test.o
+--- /dev/null
++++ b/drivers/mailbox/starfive_mailbox-test.c
+@@ -0,0 +1,407 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2015 ST Microelectronics
++ *
++ * Author: Lee Jones <lee.jones@linaro.org>
++ */
++
++#include <linux/debugfs.h>
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/mailbox_client.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/sched/signal.h>
++
++#include <linux/mailbox_controller.h>
++
++#define MBOX_MAX_SIG_LEN 8
++#define MBOX_MAX_MSG_LEN 16
++#define MBOX_BYTES_PER_LINE 16
++#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2)
++#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
++
++static bool mbox_data_ready;
++
++struct mbox_test_device {
++ struct device *dev;
++ void __iomem *tx_mmio;
++ void __iomem *rx_mmio;
++ struct mbox_chan *tx_channel;
++ struct mbox_chan *rx_channel;
++ char *rx_buffer;
++ char *signal;
++ char *message;
++ spinlock_t lock;
++ wait_queue_head_t waitq;
++ struct fasync_struct *async_queue;
++ struct dentry *root_debugfs_dir;
++};
++
++static ssize_t mbox_test_signal_write(struct file *filp,
++ const char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct mbox_test_device *tdev = filp->private_data;
++
++ if (!tdev->tx_channel) {
++ dev_err(tdev->dev, "Channel cannot do Tx\n");
++ return -EINVAL;
++ }
++
++ if (count > MBOX_MAX_SIG_LEN) {
++ dev_err(tdev->dev,
++ "Signal length %zd greater than max allowed %d\n",
++ count, MBOX_MAX_SIG_LEN);
++ return -EINVAL;
++ }
++
++ /* Only allocate memory if we need to */
++ if (!tdev->signal) {
++ tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL);
++ if (!tdev->signal)
++ return -ENOMEM;
++ }
++
++ if (copy_from_user(tdev->signal, userbuf, count)) {
++ kfree(tdev->signal);
++ tdev->signal = NULL;
++ return -EFAULT;
++ }
++
++ return count;
++}
++
++static const struct file_operations mbox_test_signal_ops = {
++ .write = mbox_test_signal_write,
++ .open = simple_open,
++ .llseek = generic_file_llseek,
++};
++
++static int mbox_test_message_fasync(int fd, struct file *filp, int on)
++{
++ struct mbox_test_device *tdev = filp->private_data;
++
++ return fasync_helper(fd, filp, on, &tdev->async_queue);
++}
++
++static ssize_t mbox_test_message_write(struct file *filp,
++ const char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct mbox_test_device *tdev = filp->private_data;
++ void *data;
++ int ret;
++
++ if (!tdev->tx_channel) {
++ dev_err(tdev->dev, "Channel cannot do Tx\n");
++ return -EINVAL;
++ }
++
++ if (count > MBOX_MAX_MSG_LEN) {
++ dev_err(tdev->dev,
++ "Message length %zd greater than max allowed %d\n",
++ count, MBOX_MAX_MSG_LEN);
++ return -EINVAL;
++ }
++
++ tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL);
++ if (!tdev->message)
++ return -ENOMEM;
++
++ ret = copy_from_user(tdev->message, userbuf, count);
++ if (ret) {
++ ret = -EFAULT;
++ goto out;
++ }
++
++ if (tdev->tx_mmio && tdev->signal) {
++ print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS,
++ tdev->signal, MBOX_MAX_SIG_LEN);
++
++ data = tdev->signal;
++ } else
++ data = tdev->message;
++
++ print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS,
++ tdev->message, MBOX_MAX_MSG_LEN);
++
++ ret = mbox_send_message(tdev->tx_channel, data);
++ mbox_chan_txdone(tdev->tx_channel, ret);
++ if (ret < 0)
++ dev_err(tdev->dev, "Failed to send message via mailbox\n");
++
++out:
++ kfree(tdev->signal);
++ kfree(tdev->message);
++ tdev->signal = NULL;
++
++ return ret < 0 ? ret : count;
++}
++
++static bool mbox_test_message_data_ready(struct mbox_test_device *tdev)
++{
++ bool data_ready;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tdev->lock, flags);
++ data_ready = mbox_data_ready;
++ spin_unlock_irqrestore(&tdev->lock, flags);
++
++ return data_ready;
++}
++
++static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct mbox_test_device *tdev = filp->private_data;
++ unsigned long flags;
++ char *touser, *ptr;
++ int ret;
++
++ touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL);
++ if (!touser)
++ return -ENOMEM;
++
++ if (!tdev->rx_channel) {
++ ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n");
++ ret = simple_read_from_buffer(userbuf, count, ppos,
++ touser, ret);
++ goto kfree_err;
++ }
++
++ do {
++ if (mbox_test_message_data_ready(tdev))
++ break;
++
++ if (filp->f_flags & O_NONBLOCK) {
++ ret = -EAGAIN;
++ goto waitq_err;
++ }
++
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ goto waitq_err;
++ }
++ schedule();
++
++ } while (1);
++
++ spin_lock_irqsave(&tdev->lock, flags);
++
++ ptr = tdev->rx_buffer;
++
++ mbox_data_ready = false;
++
++ spin_unlock_irqrestore(&tdev->lock, flags);
++ if (copy_to_user((void __user *)userbuf, ptr, 4))
++ ret = -EFAULT;
++
++waitq_err:
++ __set_current_state(TASK_RUNNING);
++kfree_err:
++ kfree(touser);
++ return ret;
++}
++
++static __poll_t
++mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait)
++{
++ struct mbox_test_device *tdev = filp->private_data;
++
++ poll_wait(filp, &tdev->waitq, wait);
++
++ if (mbox_test_message_data_ready(tdev))
++ return EPOLLIN | EPOLLRDNORM;
++ return 0;
++}
++
++static const struct file_operations mbox_test_message_ops = {
++ .write = mbox_test_message_write,
++ .read = mbox_test_message_read,
++ .fasync = mbox_test_message_fasync,
++ .poll = mbox_test_message_poll,
++ .open = simple_open,
++ .llseek = generic_file_llseek,
++};
++
++static int mbox_test_add_debugfs(struct platform_device *pdev,
++ struct mbox_test_device *tdev)
++{
++ if (!debugfs_initialized())
++ return 0;
++
++ tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL);
++ if (!tdev->root_debugfs_dir) {
++ dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
++ return -EINVAL;
++ }
++
++ debugfs_create_file("message", 0600, tdev->root_debugfs_dir,
++ tdev, &mbox_test_message_ops);
++
++ debugfs_create_file("signal", 0200, tdev->root_debugfs_dir,
++ tdev, &mbox_test_signal_ops);
++
++ return 0;
++}
++
++static void mbox_test_receive_message(struct mbox_client *client, void *message)
++{
++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
++ unsigned long flags;
++
++ spin_lock_irqsave(&tdev->lock, flags);
++ if (tdev->rx_mmio) {
++ memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN);
++ print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS,
++ tdev->rx_buffer, MBOX_MAX_MSG_LEN);
++ } else if (message) {
++ print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS,
++ message, MBOX_MAX_MSG_LEN);
++ memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN);
++ }
++ mbox_data_ready = true;
++ spin_unlock_irqrestore(&tdev->lock, flags);
++}
++
++static void mbox_test_prepare_message(struct mbox_client *client, void *message)
++{
++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
++
++ if (tdev->tx_mmio) {
++ if (tdev->signal)
++ memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN);
++ else
++ memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN);
++ }
++}
++
++static struct mbox_chan *
++mbox_test_request_channel(struct platform_device *pdev, const char *name)
++{
++ struct mbox_client *client;
++ struct mbox_chan *channel;
++
++ client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
++ if (!client)
++ return ERR_PTR(-ENOMEM);
++
++ client->dev = &pdev->dev;
++ client->rx_callback = mbox_test_receive_message;
++ client->tx_prepare = mbox_test_prepare_message;
++ client->tx_block = false;
++ client->knows_txdone = false;
++ client->tx_tout = 500;
++
++ channel = mbox_request_channel_byname(client, name);
++ if (IS_ERR(channel)) {
++ dev_warn(&pdev->dev, "Failed to request %s channel\n", name);
++ return NULL;
++ }
++
++ return channel;
++}
++
++static int mbox_test_probe(struct platform_device *pdev)
++{
++ struct mbox_test_device *tdev;
++ struct resource *res;
++ resource_size_t size;
++ int ret;
++
++ tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
++ if (!tdev)
++ return -ENOMEM;
++
++ /* It's okay for MMIO to be NULL */
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
++ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
++ /* if reserved area in SRAM, try just ioremap */
++ size = resource_size(res);
++ tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
++ } else if (IS_ERR(tdev->tx_mmio)) {
++ tdev->tx_mmio = NULL;
++ }
++
++ /* If specified, second reg entry is Rx MMIO */
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
++ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
++ size = resource_size(res);
++ tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
++ } else if (IS_ERR(tdev->rx_mmio)) {
++ tdev->rx_mmio = tdev->tx_mmio;
++ }
++
++ tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
++ tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
++
++ if (!tdev->tx_channel && !tdev->rx_channel)
++ return -EPROBE_DEFER;
++
++ /* If Rx is not specified but has Rx MMIO, then Rx = Tx */
++ if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio))
++ tdev->rx_channel = tdev->tx_channel;
++
++ tdev->dev = &pdev->dev;
++ platform_set_drvdata(pdev, tdev);
++
++ spin_lock_init(&tdev->lock);
++
++ if (tdev->rx_channel) {
++ tdev->rx_buffer = devm_kzalloc(&pdev->dev,
++ MBOX_MAX_MSG_LEN, GFP_KERNEL);
++ if (!tdev->rx_buffer)
++ return -ENOMEM;
++ }
++
++ ret = mbox_test_add_debugfs(pdev, tdev);
++ if (ret)
++ return ret;
++
++ dev_info(&pdev->dev, "Successfully registered\n");
++
++ return 0;
++}
++
++static int mbox_test_remove(struct platform_device *pdev)
++{
++ struct mbox_test_device *tdev = platform_get_drvdata(pdev);
++
++ debugfs_remove_recursive(tdev->root_debugfs_dir);
++
++ if (tdev->tx_channel)
++ mbox_free_channel(tdev->tx_channel);
++ if (tdev->rx_channel)
++ mbox_free_channel(tdev->rx_channel);
++
++ return 0;
++}
++
++static const struct of_device_id mbox_test_match[] = {
++ { .compatible = "starfive,mailbox-test" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, mbox_test_match);
++
++static struct platform_driver mbox_test_driver = {
++ .driver = {
++ .name = "mailbox_test",
++ .of_match_table = mbox_test_match,
++ },
++ .probe = mbox_test_probe,
++ .remove = mbox_test_remove,
++};
++module_platform_driver(mbox_test_driver);
++
++MODULE_DESCRIPTION("Generic Mailbox Testing Facility");
++MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/mailbox/starfive_mailbox.c
+@@ -0,0 +1,347 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * mailbox driver for StarFive JH7110 SoC
++ *
++ * Copyright (c) 2021 StarFive Technology Co., Ltd.
++ * Author: Shanlong Li <shanlong.li@starfivetech.com>
++ */
++
++#include <linux/bitops.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/mailbox_controller.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/pm_runtime.h>
++
++#include "mailbox.h"
++
++#define MBOX_CHAN_MAX 4
++
++#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x10))
++#define MBOX_IRQ_REG 0x00
++#define MBOX_SET_REG 0x04
++#define MBOX_CLR_REG 0x08
++#define MBOX_CMD_REG 0x0c
++#define MBC_PEND_SMRY 0x100
++
++typedef enum {
++ MAILBOX_CORE_U7 = 0,
++ MAILBOX_CORE_HIFI4,
++ MAILBOX_CORE_E2,
++ MAILBOX_CORE_RSVD0,
++ MAILBOX_CORE_NUM,
++} mailbox_core_t;
++
++struct mailbox_irq_name_c{
++ int id;
++ char name[16];
++};
++
++static const struct mailbox_irq_name_c irq_peer_name[MBOX_CHAN_MAX] = {
++ {MAILBOX_CORE_U7, "u74_core"},
++ {MAILBOX_CORE_HIFI4, "hifi4_core"},
++ {MAILBOX_CORE_E2, "e24_core"},
++ {MAILBOX_CORE_RSVD0, "" },
++};
++
++/**
++ * starfive mailbox channel information
++ *
++ * A channel can be used for TX or RX, it can trigger remote
++ * processor interrupt to notify remote processor and can receive
++ * interrupt if has incoming message.
++ *
++ * @dst_irq: Interrupt vector for remote processor
++ * @core_id: id for remote processor
++ */
++struct starfive_chan_info {
++ unsigned int dst_irq;
++ mailbox_core_t core_id;
++};
++
++/**
++ * starfive mailbox controller data
++ *
++ * Mailbox controller includes 4 channels and can allocate
++ * channel for message transferring.
++ *
++ * @dev: Device to which it is attached
++ * @base: Base address of the register mapping region
++ * @chan: Representation of channels in mailbox controller
++ * @mchan: Representation of channel info
++ * @controller: Representation of a communication channel controller
++ */
++struct starfive_mbox {
++ struct device *dev;
++ void __iomem *base;
++ struct mbox_chan chan[MBOX_CHAN_MAX];
++ struct starfive_chan_info mchan[MBOX_CHAN_MAX];
++ struct mbox_controller controller;
++ struct clk *clk;
++ struct reset_control *rst_rresetn;
++};
++
++static struct starfive_mbox *to_starfive_mbox(struct mbox_controller *mbox)
++{
++ return container_of(mbox, struct starfive_mbox, controller);
++}
++
++static struct mbox_chan *
++starfive_of_mbox_index_xlate(struct mbox_controller *mbox,
++ const struct of_phandle_args *sp)
++{
++ struct starfive_mbox *sbox;
++
++ int ind = sp->args[0];
++ int core_id = sp->args[1];
++
++ if (ind >= mbox->num_chans || core_id >= MAILBOX_CORE_NUM)
++ return ERR_PTR(-EINVAL);
++
++ sbox = to_starfive_mbox(mbox);
++
++ sbox->mchan[ind].core_id = core_id;
++
++ return &mbox->chans[ind];
++}
++
++static irqreturn_t starfive_rx_irq_handler(int irq, void *p)
++{
++ struct mbox_chan *chan = p;
++ unsigned long ch = (unsigned long)chan->con_priv;
++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++ void __iomem *base = MBOX_BASE(mbox, ch);
++ u32 val;
++
++ val = readl(base + MBOX_CMD_REG);
++ if (!val)
++ return IRQ_NONE;
++
++ mbox_chan_received_data(chan, (void *)&val);
++ writel(val, base + MBOX_CLR_REG);
++ return IRQ_HANDLED;
++}
++
++static int starfive_mbox_check_state(struct mbox_chan *chan)
++{
++ unsigned long ch = (unsigned long)chan->con_priv;
++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++ unsigned long irq_flag = IRQF_SHARED;
++ long ret = 0;
++
++ pm_runtime_get_sync(mbox->dev);
++ /* MAILBOX should be with IRQF_NO_SUSPEND set */
++ if (!mbox->dev->pm_domain)
++ irq_flag |= IRQF_NO_SUSPEND;
++
++ /* Mailbox is idle so directly bail out */
++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch))
++ return -EBUSY;
++
++ if (mbox->mchan[ch].dst_irq > 0) {
++ dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch);
++ ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler,
++ irq_flag, irq_peer_name[ch].name, chan);
++ if (ret < 0)
++ dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq);
++ }
++
++ return ret;
++}
++
++static int starfive_mbox_startup(struct mbox_chan *chan)
++{
++ return starfive_mbox_check_state(chan);
++}
++
++static void starfive_mbox_shutdown(struct mbox_chan *chan)
++{
++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++ unsigned long ch = (unsigned long)chan->con_priv;
++ void __iomem *base = MBOX_BASE(mbox, ch);
++
++ writel(0x0, base + MBOX_IRQ_REG);
++ writel(0x0, base + MBOX_CLR_REG);
++
++ if (mbox->mchan[ch].dst_irq > 0)
++ devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan);
++ pm_runtime_put_sync(mbox->dev);
++}
++
++static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg)
++{
++ unsigned long ch = (unsigned long)chan->con_priv;
++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++ struct starfive_chan_info *mchan = &mbox->mchan[ch];
++ void __iomem *base = MBOX_BASE(mbox, ch);
++ u32 *buf = msg;
++
++ /* Ensure channel is released */
++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) {
++ pr_debug("%s:%d. busy\n", __func__, __LINE__);
++ return -EBUSY;
++ }
++
++ /* Clear mask for destination interrupt */
++ writel(BIT(mchan->core_id), base + MBOX_IRQ_REG);
++
++ /* Fill message data */
++ writel(*buf, base + MBOX_SET_REG);
++ return 0;
++}
++
++static struct mbox_chan_ops starfive_mbox_ops = {
++ .startup = starfive_mbox_startup,
++ .send_data = starfive_mbox_send_data,
++ .shutdown = starfive_mbox_shutdown,
++};
++
++static const struct of_device_id starfive_mbox_of_match[] = {
++ { .compatible = "starfive,mail_box",},
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, starfive_mbox_of_match);
++
++void starfive_mailbox_init(struct starfive_mbox *mbox)
++{
++ mbox->clk = devm_clk_get_optional(mbox->dev, "clk_apb");
++ if (IS_ERR(mbox->clk)) {
++ dev_err(mbox->dev, "failed to get mailbox\n");
++ return;
++ }
++
++ mbox->rst_rresetn = devm_reset_control_get_exclusive(mbox->dev, "mbx_rre");
++ if (IS_ERR(mbox->rst_rresetn)) {
++ dev_err(mbox->dev, "failed to get mailbox reset\n");
++ return;
++ }
++
++ clk_prepare_enable(mbox->clk);
++ reset_control_deassert(mbox->rst_rresetn);
++}
++
++static int starfive_mbox_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct starfive_mbox *mbox;
++ struct mbox_chan *chan;
++ struct resource *res;
++ unsigned long ch;
++ int err;
++
++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
++ if (!mbox)
++ return -ENOMEM;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ mbox->base = devm_ioremap_resource(dev, res);
++ mbox->dev = dev;
++
++ if (IS_ERR(mbox->base))
++ return PTR_ERR(mbox->base);
++
++ starfive_mailbox_init(mbox);
++
++ mbox->controller.dev = dev;
++ mbox->controller.chans = mbox->chan;
++ mbox->controller.num_chans = MBOX_CHAN_MAX;
++ mbox->controller.ops = &starfive_mbox_ops;
++ mbox->controller.of_xlate = starfive_of_mbox_index_xlate;
++ mbox->controller.txdone_irq = true;
++ mbox->controller.txdone_poll = false;
++
++ /* Initialize mailbox channel data */
++ chan = mbox->chan;
++ for (ch = 0; ch < MBOX_CHAN_MAX; ch++) {
++ mbox->mchan[ch].dst_irq = 0;
++ mbox->mchan[ch].core_id = (mailbox_core_t)ch;
++ chan[ch].con_priv = (void *)ch;
++ }
++ mbox->mchan[MAILBOX_CORE_HIFI4].dst_irq = platform_get_irq(pdev, 0);
++ mbox->mchan[MAILBOX_CORE_E2].dst_irq = platform_get_irq(pdev, 1);
++
++ err = mbox_controller_register(&mbox->controller);
++ if (err) {
++ dev_err(dev, "Failed to register mailbox %d\n", err);
++ return err;
++ }
++
++ platform_set_drvdata(pdev, mbox);
++ dev_info(dev, "Mailbox enabled\n");
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
++ return 0;
++}
++
++static int starfive_mbox_remove(struct platform_device *pdev)
++{
++ struct starfive_mbox *mbox = platform_get_drvdata(pdev);
++
++ mbox_controller_unregister(&mbox->controller);
++ devm_clk_put(mbox->dev, mbox->clk);
++ pm_runtime_disable(mbox->dev);
++
++ return 0;
++}
++
++static int __maybe_unused starfive_mbox_suspend(struct device *dev)
++{
++ struct starfive_mbox *mbox = dev_get_drvdata(dev);
++
++ clk_disable_unprepare(mbox->clk);
++
++ return 0;
++}
++
++static int __maybe_unused starfive_mbox_resume(struct device *dev)
++{
++ struct starfive_mbox *mbox = dev_get_drvdata(dev);
++ int ret;
++
++ ret = clk_prepare_enable(mbox->clk);
++ if (ret)
++ dev_err(dev, "failed to enable clock\n");
++
++ return ret;
++}
++
++static const struct dev_pm_ops starfive_mbox_pm_ops = {
++ .suspend = starfive_mbox_suspend,
++ .resume = starfive_mbox_resume,
++ SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL)
++};
++static struct platform_driver starfive_mbox_driver = {
++ .probe = starfive_mbox_probe,
++ .remove = starfive_mbox_remove,
++ .driver = {
++ .name = "mailbox",
++ .of_match_table = starfive_mbox_of_match,
++ .pm = &starfive_mbox_pm_ops,
++ },
++};
++
++static int __init starfive_mbox_init(void)
++{
++ return platform_driver_register(&starfive_mbox_driver);
++}
++core_initcall(starfive_mbox_init);
++
++static void __exit starfive_mbox_exit(void)
++{
++ platform_driver_unregister(&starfive_mbox_driver);
++}
++module_exit(starfive_mbox_exit);
++
++MODULE_DESCRIPTION("StarFive Mailbox Controller driver");
++MODULE_AUTHOR("Shanlong Li <shanlong.li@starfivetech.com>");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 0f44bd6bec708782f38bba4d03deecf927d1c83d Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfivetech.com>
+Date: Fri, 9 Jun 2023 15:31:53 +0800
+Subject: [PATCH 065/116] driver: rtc: Add StarFive JH7110 rtc driver
+
+Add RTC driver and support for StarFive JH7110 SoC.
+
+Signed-off-by: ziv.xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/rtc/Kconfig | 8 +
+ drivers/rtc/Makefile | 1 +
+ drivers/rtc/rtc-starfive.c | 743 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 752 insertions(+)
+ create mode 100644 drivers/rtc/rtc-starfive.c
+
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -1327,6 +1327,14 @@ config RTC_DRV_NTXEC
+ embedded controller found in certain e-book readers designed by the
+ original design manufacturer Netronix.
+
++config RTC_DRV_STARFIVE
++ tristate "StarFive 32.768k-RTC"
++ depends on ARCH_STARFIVE
++ depends on OF
++ help
++ If you say Y here you will get support for the RTC found on
++ StarFive SOCS.
++
+ comment "on-CPU RTC drivers"
+
+ config RTC_DRV_ASM9260
+--- a/drivers/rtc/Makefile
++++ b/drivers/rtc/Makefile
+@@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
+ obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
+ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
++obj-$(CONFIG_RTC_DRV_STARFIVE) += rtc-starfive.o
+ obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
+ obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
+ obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
+--- /dev/null
++++ b/drivers/rtc/rtc-starfive.c
+@@ -0,0 +1,743 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * RTC driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2021 StarFive Technology Co., Ltd.
++ */
++
++#include <asm/delay.h>
++#include <linux/bcd.h>
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/completion.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/iopoll.h>
++#include <linux/platform_device.h>
++#include <linux/rtc.h>
++
++/* Registers */
++#define SFT_RTC_CFG 0x00
++#define SFT_RTC_SW_CAL_VALUE 0x04
++#define SFT_RTC_HW_CAL_CFG 0x08
++#define SFT_RTC_CMP_CFG 0x0C
++#define SFT_RTC_IRQ_EN 0x10
++#define SFT_RTC_IRQ_EVEVT 0x14
++#define SFT_RTC_IRQ_STATUS 0x18
++#define SFT_RTC_CAL_VALUE 0x24
++#define SFT_RTC_CFG_TIME 0x28
++#define SFT_RTC_CFG_DATE 0x2C
++#define SFT_RTC_ACT_TIME 0x34
++#define SFT_RTC_ACT_DATE 0x38
++#define SFT_RTC_TIME 0x3C
++#define SFT_RTC_DATE 0x40
++#define SFT_RTC_TIME_LATCH 0x44
++#define SFT_RTC_DATE_LATCH 0x48
++
++/* RTC_CFG */
++#define RTC_CFG_ENABLE_SHIFT 0 /* RW: RTC Enable. */
++#define RTC_CFG_CAL_EN_HW_SHIFT 1 /* RW: Enable of hardware calibretion. */
++#define RTC_CFG_CAL_SEL_SHIFT 2 /* RW: select the hw/sw calibretion mode.*/
++#define RTC_CFG_HOUR_MODE_SHIFT 3 /* RW: time hour mode. 24h|12h */
++
++/* RTC_SW_CAL_VALUE */
++#define RTC_SW_CAL_VALUE_MASK GENMASK(15, 0)
++#define RTC_SW_CAL_MAX RTC_SW_CAL_VALUE_MASK
++#define RTC_SW_CAL_MIN 0
++#define RTC_TICKS_PER_SEC 32768 /* Number of ticks per second */
++#define RTC_PPB_MULT 1000000000LL /* Multiplier for ppb conversions */
++
++/* RTC_HW_CAL_CFG */
++#define RTC_HW_CAL_REF_SEL_SHIFT 0
++#define RTC_HW_CAL_FRQ_SEL_SHIFT 1
++
++/* IRQ_EN/IRQ_EVEVT/IRQ_STATUS */
++#define RTC_IRQ_CAL_START BIT(0)
++#define RTC_IRQ_CAL_FINISH BIT(1)
++#define RTC_IRQ_CMP BIT(2)
++#define RTC_IRQ_1SEC BIT(3)
++#define RTC_IRQ_ALAEM BIT(4)
++#define RTC_IRQ_EVT_UPDATE_PSE BIT(31) /* WO: Enable of update time&&date, IRQ_EVEVT only */
++#define RTC_IRQ_ALL (RTC_IRQ_CAL_START \
++ | RTC_IRQ_CAL_FINISH \
++ | RTC_IRQ_CMP \
++ | RTC_IRQ_1SEC \
++ | RTC_IRQ_ALAEM)
++
++/* CAL_VALUE */
++#define RTC_CAL_VALUE_MASK GENMASK(15, 0)
++
++/* CFG_TIME/ACT_TIME/RTC_TIME */
++#define TIME_SEC_MASK GENMASK(6, 0)
++#define TIME_MIN_MASK GENMASK(13, 7)
++#define TIME_HOUR_MASK GENMASK(20, 14)
++
++/* CFG_DATE/ACT_DATE/RTC_DATE */
++#define DATE_DAY_MASK GENMASK(5, 0)
++#define DATE_MON_MASK GENMASK(10, 6)
++#define DATE_YEAR_MASK GENMASK(18, 11)
++
++#define INT_TIMEOUT_US 180
++
++enum RTC_HOUR_MODE {
++ RTC_HOUR_MODE_12H = 0,
++ RTC_HOUR_MODE_24H = 1
++};
++
++enum RTC_CAL_MODE {
++ RTC_CAL_MODE_SW = 0,
++ RTC_CAL_MODE_HW = 1
++};
++
++enum RTC_HW_CAL_REF_MODE {
++ RTC_CAL_CLK_REF = 0,
++ RTC_CAL_CLK_MARK = 1
++};
++
++static const unsigned long refclk_list[] = {
++ 1000000,
++ 2000000,
++ 4000000,
++ 5927000,
++ 6000000,
++ 7200000,
++ 8000000,
++ 10250000,
++ 11059200,
++ 12000000,
++ 12288000,
++ 13560000,
++ 16000000,
++ 19200000,
++ 20000000,
++ 22118000,
++ 24000000,
++ 24567000,
++ 25000000,
++ 26000000,
++ 27000000,
++ 30000000,
++ 32000000,
++ 33868800,
++ 36000000,
++ 36860000,
++ 40000000,
++ 44000000,
++ 50000000,
++ 54000000,
++ 28224000,
++ 28000000,
++};
++
++struct sft_rtc {
++ struct rtc_device *rtc_dev;
++ struct completion cal_done;
++ struct completion onesec_done;
++ struct clk *pclk;
++ struct clk *cal_clk;
++ struct reset_control *rst_array;
++ int hw_cal_map;
++ void __iomem *regs;
++ int rtc_irq;
++ int ms_pulse_irq;
++ int one_sec_pulse_irq;
++};
++
++static inline void sft_rtc_set_enabled(struct sft_rtc *srtc, bool enabled)
++{
++ u32 val;
++
++ if (enabled) {
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val |= BIT(RTC_CFG_ENABLE_SHIFT);
++ writel(val, srtc->regs + SFT_RTC_CFG);
++ } else {
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val &= ~BIT(RTC_CFG_ENABLE_SHIFT);
++ writel(val, srtc->regs + SFT_RTC_CFG);
++ }
++}
++
++static inline bool sft_rtc_get_enabled(struct sft_rtc *srtc)
++{
++ return !!(readl(srtc->regs + SFT_RTC_CFG) & BIT(RTC_CFG_ENABLE_SHIFT));
++}
++
++static inline void sft_rtc_set_mode(struct sft_rtc *srtc, enum RTC_HOUR_MODE mode)
++{
++ u32 val;
++
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val |= mode << RTC_CFG_HOUR_MODE_SHIFT;
++ writel(val, srtc->regs + SFT_RTC_CFG);
++}
++
++static inline int sft_rtc_irq_enable(struct sft_rtc *srtc, u32 irq, bool enable)
++{
++ u32 val;
++
++ if (!(irq & RTC_IRQ_ALL))
++ return -EINVAL;
++
++ if (enable) {
++ val = readl(srtc->regs + SFT_RTC_IRQ_EN);
++ val |= irq;
++ writel(val, srtc->regs + SFT_RTC_IRQ_EN);
++ } else {
++ val = readl(srtc->regs + SFT_RTC_IRQ_EN);
++ val &= ~irq;
++ writel(val, srtc->regs + SFT_RTC_IRQ_EN);
++ }
++ return 0;
++}
++
++static inline void
++sft_rtc_set_cal_hw_enable(struct sft_rtc *srtc, bool enable)
++{
++ u32 val;
++
++ if (enable) {
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val |= BIT(RTC_CFG_CAL_EN_HW_SHIFT);
++ writel(val, srtc->regs + SFT_RTC_CFG);
++ } else {
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val &= ~BIT(RTC_CFG_CAL_EN_HW_SHIFT);
++ writel(val, srtc->regs + SFT_RTC_CFG);
++ }
++}
++
++static inline void
++sft_rtc_set_cal_mode(struct sft_rtc *srtc, enum RTC_CAL_MODE mode)
++{
++ u32 val;
++
++ val = readl(srtc->regs + SFT_RTC_CFG);
++ val |= mode << RTC_CFG_CAL_SEL_SHIFT;
++ writel(val, srtc->regs + SFT_RTC_CFG);
++}
++
++static int sft_rtc_get_hw_calclk(struct device *dev, unsigned long freq)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(refclk_list); i++)
++ if (refclk_list[i] == freq)
++ return i;
++
++ dev_err(dev, "refclk: %ldHz do not support.\n", freq);
++ return -EINVAL;
++}
++
++static inline void sft_rtc_reg2time(struct rtc_time *tm, u32 reg)
++{
++ tm->tm_hour = bcd2bin(FIELD_GET(TIME_HOUR_MASK, reg));
++ tm->tm_min = bcd2bin(FIELD_GET(TIME_MIN_MASK, reg));
++ tm->tm_sec = bcd2bin(FIELD_GET(TIME_SEC_MASK, reg));
++}
++
++static inline void sft_rtc_reg2date(struct rtc_time *tm, u32 reg)
++{
++ tm->tm_year = bcd2bin(FIELD_GET(DATE_YEAR_MASK, reg)) + 100;
++ tm->tm_mon = bcd2bin(FIELD_GET(DATE_MON_MASK, reg)) - 1;
++ tm->tm_mday = bcd2bin(FIELD_GET(DATE_DAY_MASK, reg));
++}
++
++static inline u32 sft_rtc_time2reg(struct rtc_time *tm)
++{
++ return FIELD_PREP(TIME_HOUR_MASK, bin2bcd(tm->tm_hour)) |
++ FIELD_PREP(TIME_MIN_MASK, bin2bcd(tm->tm_min)) |
++ FIELD_PREP(TIME_SEC_MASK, bin2bcd(tm->tm_sec));
++}
++
++static inline u32 sft_rtc_date2reg(struct rtc_time *tm)
++{
++ return FIELD_PREP(DATE_YEAR_MASK, bin2bcd(tm->tm_year - 100)) |
++ FIELD_PREP(DATE_MON_MASK, bin2bcd(tm->tm_mon + 1)) |
++ FIELD_PREP(DATE_DAY_MASK, bin2bcd(tm->tm_mday));
++}
++
++static inline void sft_rtc_update_pulse(struct sft_rtc *srtc)
++{
++ u32 val;
++
++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT);
++ val |= RTC_IRQ_EVT_UPDATE_PSE;
++ writel(val, srtc->regs + SFT_RTC_IRQ_EVEVT);
++}
++
++static irqreturn_t sft_rtc_irq_handler(int irq, void *data)
++{
++ struct sft_rtc *srtc = data;
++ struct timerqueue_node *next;
++ u32 irq_flags = 0;
++ u32 irq_mask = 0;
++ u32 val;
++ int ret = 0;
++
++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT);
++ if (val & RTC_IRQ_CAL_START)
++ irq_mask |= RTC_IRQ_CAL_START;
++
++ if (val & RTC_IRQ_CAL_FINISH) {
++ irq_mask |= RTC_IRQ_CAL_FINISH;
++ complete(&srtc->cal_done);
++ }
++
++ if (val & RTC_IRQ_CMP)
++ irq_mask |= RTC_IRQ_CMP;
++
++ if (val & RTC_IRQ_1SEC) {
++ irq_flags |= RTC_PF;
++ irq_mask |= RTC_IRQ_1SEC;
++ complete(&srtc->onesec_done);
++ }
++
++ if (val & RTC_IRQ_ALAEM) {
++ irq_flags |= RTC_AF;
++ irq_mask |= RTC_IRQ_ALAEM;
++
++ next = timerqueue_getnext(&srtc->rtc_dev->timerqueue);
++ if (next == &srtc->rtc_dev->aie_timer.node)
++ dev_info(&srtc->rtc_dev->dev, "alarm expires");
++ }
++
++ writel(irq_mask, srtc->regs + SFT_RTC_IRQ_EVEVT);
++
++ /* Wait interrupt flag clear */
++ ret = readl_poll_timeout_atomic(srtc->regs + SFT_RTC_IRQ_EVEVT, val,
++ (val & irq_mask) == 0, 0, INT_TIMEOUT_US);
++ if (ret)
++ dev_warn(&srtc->rtc_dev->dev, "fail to clear rtc interrupt flag\n");
++
++ if (irq_flags)
++ rtc_update_irq(srtc->rtc_dev, 1, irq_flags | RTC_IRQF);
++
++ return IRQ_HANDLED;
++}
++
++static int sft_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ u32 val;
++ int irq_1sec_state_start, irq_1sec_state_end;
++
++ /* If the RTC is disabled, assume the values are invalid */
++ if (!sft_rtc_get_enabled(srtc))
++ return -EINVAL;
++
++ irq_1sec_state_start =
++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1;
++
++read_again:
++ val = readl(srtc->regs + SFT_RTC_TIME);
++ sft_rtc_reg2time(tm, val);
++
++ val = readl(srtc->regs + SFT_RTC_DATE);
++ sft_rtc_reg2date(tm, val);
++
++ if (irq_1sec_state_start == 0) {
++ irq_1sec_state_end =
++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1;
++ if (irq_1sec_state_end == 1) {
++ irq_1sec_state_start = 1;
++ goto read_again;
++ }
++ }
++
++ return 0;
++}
++
++static int sft_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ u32 val;
++ int ret;
++
++ val = sft_rtc_time2reg(tm);
++ writel(val, srtc->regs + SFT_RTC_CFG_TIME);
++
++ val = sft_rtc_date2reg(tm);
++ writel(val, srtc->regs + SFT_RTC_CFG_DATE);
++
++ /* Update pulse */
++ sft_rtc_update_pulse(srtc);
++
++ /* Ensure that data is fully written */
++ ret = wait_for_completion_interruptible_timeout(&srtc->onesec_done,
++ usecs_to_jiffies(120));
++ if (ret) {
++ dev_warn(dev,
++ "rtc wait for completion interruptible timeout.\n");
++ }
++ return 0;
++}
++
++static int sft_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++ return sft_rtc_irq_enable(srtc, RTC_IRQ_ALAEM, enabled);
++}
++
++static int sft_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ u32 val;
++
++ val = readl(srtc->regs + SFT_RTC_ACT_TIME);
++ sft_rtc_reg2time(&alarm->time, val);
++
++ val = readl(srtc->regs + SFT_RTC_ACT_DATE);
++ sft_rtc_reg2date(&alarm->time, val);
++
++ return 0;
++}
++
++static int sft_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ u32 val;
++
++ sft_rtc_alarm_irq_enable(dev, 0);
++
++ val = sft_rtc_time2reg(&alarm->time);
++ writel(val, srtc->regs + SFT_RTC_ACT_TIME);
++
++ val = sft_rtc_date2reg(&alarm->time);
++ writel(val, srtc->regs + SFT_RTC_ACT_DATE);
++
++ sft_rtc_alarm_irq_enable(dev, alarm->enabled);
++
++ return 0;
++}
++
++static int sft_rtc_get_offset(struct device *dev, long *offset)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ s64 tmp;
++ u32 val;
++
++ val = readl(srtc->regs + SFT_RTC_CAL_VALUE)
++ & RTC_SW_CAL_VALUE_MASK;
++ val += 1;
++ /*
++ * the adjust val range is [0x0000-0xffff],
++ * the default val is 0x7fff (32768-1),mapping offset=0 ;
++ */
++ tmp = (s64)val - RTC_TICKS_PER_SEC;
++ tmp *= RTC_PPB_MULT;
++ tmp = div_s64(tmp, RTC_TICKS_PER_SEC);
++
++ /* Offset value operates in negative way, so swap sign */
++ *offset = -tmp;
++
++ return 0;
++}
++
++static int sft_rtc_set_offset(struct device *dev, long offset)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ s64 tmp;
++ u32 val;
++
++ tmp = offset * RTC_TICKS_PER_SEC;
++ tmp = div_s64(tmp, RTC_PPB_MULT);
++
++ tmp = RTC_TICKS_PER_SEC - tmp;
++ tmp -= 1;
++ if (tmp > RTC_SW_CAL_MAX || tmp < RTC_SW_CAL_MIN) {
++ dev_err(dev, "offset is out of range.\n");
++ return -EINVAL;
++ }
++
++ val = tmp & RTC_SW_CAL_VALUE_MASK;
++ /* set software calibration value */
++ writel(val, srtc->regs + SFT_RTC_SW_CAL_VALUE);
++
++ /* set CFG_RTC-cal_sel to select calibretion by software. */
++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW);
++
++ return 0;
++}
++
++static __maybe_unused int
++sft_rtc_hw_adjustment(struct device *dev, unsigned int enable)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++ u32 val;
++
++ if (srtc->hw_cal_map <= 0) {
++ dev_err(dev, "fail to get cal-clock-freq.\n");
++ return -EFAULT;
++ }
++
++ if (enable) {
++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, true);
++
++ /* Set reference clock frequency value */
++ val = readl(srtc->regs + SFT_RTC_HW_CAL_CFG);
++ val |= (srtc->hw_cal_map << RTC_HW_CAL_FRQ_SEL_SHIFT);
++ writel(val, srtc->regs + SFT_RTC_HW_CAL_CFG);
++
++ /* Set CFG_RTC-cal_sel to select calibretion by hardware. */
++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_HW);
++
++ /* Set CFG_RTC-cal_en_hw to launch hardware calibretion.*/
++ sft_rtc_set_cal_hw_enable(srtc, true);
++
++ wait_for_completion_interruptible_timeout(&srtc->cal_done,
++ usecs_to_jiffies(100));
++
++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, false);
++ } else {
++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW);
++ sft_rtc_set_cal_hw_enable(srtc, false);
++ }
++
++ return 0;
++}
++
++static int sft_rtc_get_cal_clk(struct device *dev, struct sft_rtc *srtc)
++{
++ struct device_node *np = dev->of_node;
++ unsigned long cal_clk_freq;
++ u32 freq;
++ int ret;
++
++ srtc->cal_clk = devm_clk_get(dev, "cal_clk");
++ if (IS_ERR(srtc->cal_clk))
++ return PTR_ERR(srtc->cal_clk);
++
++ clk_prepare_enable(srtc->cal_clk);
++
++ cal_clk_freq = clk_get_rate(srtc->cal_clk);
++ if (!cal_clk_freq) {
++ dev_warn(dev,
++ "get rate failed, next try to get from dts.\n");
++ ret = of_property_read_u32(np, "rtc,cal-clock-freq", &freq);
++ if (!ret) {
++ cal_clk_freq = (u64)freq;
++ } else {
++ dev_err(dev,
++ "Need rtc,cal-clock-freq define in dts.\n");
++ goto err_disable_cal_clk;
++ }
++ }
++
++ srtc->hw_cal_map = sft_rtc_get_hw_calclk(dev, cal_clk_freq);
++ if (srtc->hw_cal_map < 0) {
++ ret = srtc->hw_cal_map;
++ goto err_disable_cal_clk;
++ }
++
++ return 0;
++
++err_disable_cal_clk:
++ clk_disable_unprepare(srtc->cal_clk);
++
++ return ret;
++}
++
++static int sft_rtc_get_irq(struct platform_device *pdev, struct sft_rtc *srtc)
++{
++ int ret;
++
++ srtc->rtc_irq = platform_get_irq_byname(pdev, "rtc");
++ if (srtc->rtc_irq < 0)
++ return -EINVAL;
++
++ ret = devm_request_irq(&pdev->dev, srtc->rtc_irq,
++ sft_rtc_irq_handler, 0,
++ KBUILD_MODNAME, srtc);
++ if (ret)
++ dev_err(&pdev->dev, "Failed to request interrupt, %d\n", ret);
++
++ return ret;
++}
++
++static const struct rtc_class_ops starfive_rtc_ops = {
++ .read_time = sft_rtc_read_time,
++ .set_time = sft_rtc_set_time,
++ .read_alarm = sft_rtc_read_alarm,
++ .set_alarm = sft_rtc_set_alarm,
++ .alarm_irq_enable = sft_rtc_alarm_irq_enable,
++ .set_offset = sft_rtc_set_offset,
++ .read_offset = sft_rtc_get_offset,
++};
++
++static int sft_rtc_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct sft_rtc *srtc;
++ struct rtc_time tm;
++ struct irq_desc *desc;
++ int ret;
++
++ srtc = devm_kzalloc(dev, sizeof(*srtc), GFP_KERNEL);
++ if (!srtc)
++ return -ENOMEM;
++
++ srtc->regs = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(srtc->regs))
++ return PTR_ERR(srtc->regs);
++
++ srtc->pclk = devm_clk_get(dev, "pclk");
++ if (IS_ERR(srtc->pclk)) {
++ ret = PTR_ERR(srtc->pclk);
++ dev_err(dev,
++ "Failed to retrieve the peripheral clock, %d\n", ret);
++ return ret;
++ }
++
++ srtc->rst_array = devm_reset_control_array_get_exclusive(dev);
++ if (IS_ERR(srtc->rst_array)) {
++ ret = PTR_ERR(srtc->rst_array);
++ dev_err(dev,
++ "Failed to retrieve the rtc reset, %d\n", ret);
++ return ret;
++ }
++
++ init_completion(&srtc->cal_done);
++ init_completion(&srtc->onesec_done);
++
++ ret = clk_prepare_enable(srtc->pclk);
++ if (ret) {
++ dev_err(dev,
++ "Failed to enable the peripheral clock, %d\n", ret);
++ return ret;
++ }
++
++ ret = sft_rtc_get_cal_clk(dev, srtc);
++ if (ret)
++ goto err_disable_pclk;
++
++ ret = reset_control_deassert(srtc->rst_array);
++ if (ret) {
++ dev_err(dev,
++ "Failed to deassert rtc resets, %d\n", ret);
++ goto err_disable_cal_clk;
++ }
++
++ ret = sft_rtc_get_irq(pdev, srtc);
++ if (ret)
++ goto err_disable_cal_clk;
++
++ srtc->rtc_dev = devm_rtc_allocate_device(dev);
++ if (IS_ERR(srtc->rtc_dev))
++ return PTR_ERR(srtc->rtc_dev);
++
++ platform_set_drvdata(pdev, srtc);
++
++ /* The RTC supports 01.01.2001 - 31.12.2099 */
++ srtc->rtc_dev->range_min = mktime64(2001, 1, 1, 0, 0, 0);
++ srtc->rtc_dev->range_max = mktime64(2099, 12, 31, 23, 59, 59);
++
++ srtc->rtc_dev->ops = &starfive_rtc_ops;
++ device_init_wakeup(dev, true);
++
++ desc = irq_to_desc(srtc->rtc_irq);
++ irq_desc_get_chip(desc)->flags = IRQCHIP_SKIP_SET_WAKE;
++
++ /* Always use 24-hour mode and keep the RTC values */
++ sft_rtc_set_mode(srtc, RTC_HOUR_MODE_24H);
++
++ sft_rtc_set_enabled(srtc, true);
++
++ if (device_property_read_bool(dev, "rtc,hw-adjustment"))
++ sft_rtc_hw_adjustment(dev, true);
++
++ /*
++ * If rtc time is out of supported range, reset it to the minimum time.
++ * notice that, actual year = 1900 + tm.tm_year
++ * actual month = 1 + tm.tm_mon
++ */
++ sft_rtc_read_time(dev, &tm);
++ if (tm.tm_year < 101 || tm.tm_year > 199 || tm.tm_mon < 0 || tm.tm_mon > 11 ||
++ tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 ||
++ tm.tm_min < 0 || tm.tm_min > 59 || tm.tm_sec < 0 || tm.tm_sec > 59) {
++ rtc_time64_to_tm(srtc->rtc_dev->range_min, &tm);
++ sft_rtc_set_time(dev, &tm);
++ }
++
++ ret = devm_rtc_register_device(srtc->rtc_dev);
++ if (ret)
++ goto err_disable_wakeup;
++
++ return 0;
++
++err_disable_wakeup:
++ device_init_wakeup(dev, false);
++
++err_disable_cal_clk:
++ clk_disable_unprepare(srtc->cal_clk);
++
++err_disable_pclk:
++ clk_disable_unprepare(srtc->pclk);
++
++ return ret;
++}
++
++static int sft_rtc_remove(struct platform_device *pdev)
++{
++ struct sft_rtc *srtc = platform_get_drvdata(pdev);
++
++ sft_rtc_alarm_irq_enable(&pdev->dev, 0);
++ device_init_wakeup(&pdev->dev, 0);
++
++ clk_disable_unprepare(srtc->pclk);
++ clk_disable_unprepare(srtc->cal_clk);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int sft_rtc_suspend(struct device *dev)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++ if (device_may_wakeup(dev))
++ enable_irq_wake(srtc->rtc_irq);
++
++ return 0;
++}
++
++static int sft_rtc_resume(struct device *dev)
++{
++ struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++ if (device_may_wakeup(dev))
++ disable_irq_wake(srtc->rtc_irq);
++
++ return 0;
++}
++#endif
++
++static SIMPLE_DEV_PM_OPS(sft_rtc_pm_ops, sft_rtc_suspend, sft_rtc_resume);
++
++static const struct of_device_id sft_rtc_of_match[] = {
++ { .compatible = "starfive,jh7110-rtc" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, sft_rtc_of_match);
++
++static struct platform_driver starfive_rtc_driver = {
++ .driver = {
++ .name = "starfive-rtc",
++ .of_match_table = sft_rtc_of_match,
++ .pm = &sft_rtc_pm_ops,
++ },
++ .probe = sft_rtc_probe,
++ .remove = sft_rtc_remove,
++};
++module_platform_driver(starfive_rtc_driver);
++
++MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive RTC driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:starfive-rtc");
--- /dev/null
+From 552114b8cbbd956ad8466261b5f11b059eba82ca Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Sun, 25 Jun 2023 09:40:29 +0800
+Subject: [PATCH 066/116] uart: 8250: Add dw auto flow ctrl support
+
+Add designeware 8250 auto flow ctrl support. Enable
+it by add auto-flow-control in dts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_core.c | 2 ++
+ drivers/tty/serial/8250/8250_dw.c | 3 +++
+ drivers/tty/serial/8250/8250_port.c | 14 +++++++++++++-
+ include/linux/serial_8250.h | 1 +
+ include/uapi/linux/serial_core.h | 2 ++
+ 5 files changed, 21 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/8250/8250_core.c
++++ b/drivers/tty/serial/8250/8250_core.c
+@@ -1129,6 +1129,8 @@ int serial8250_register_8250_port(const
+ uart->dl_read = up->dl_read;
+ if (up->dl_write)
+ uart->dl_write = up->dl_write;
++ if (up->probe)
++ uart->probe = up->probe;
+
+ if (uart->port.type != PORT_8250_CIR) {
+ if (serial8250_isa_config != NULL)
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -612,6 +612,9 @@ static int dw8250_probe(struct platform_
+ data->msr_mask_off |= UART_MSR_TERI;
+ }
+
++ if (device_property_read_bool(dev, "auto-flow-control"))
++ up->probe |= UART_PROBE_AFE;
++
+ /* If there is separate baudclk, get the rate from it. */
+ data->clk = devm_clk_get_optional(dev, "baudclk");
+ if (data->clk == NULL)
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -330,6 +330,14 @@ static const struct serial8250_config ua
+ .rxtrig_bytes = {1, 8, 16, 30},
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
++ [PORT_16550A_AFE] = {
++ .name = "16550A_AFE",
++ .fifo_size = 16,
++ .tx_loadsz = 16,
++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
++ .rxtrig_bytes = {1, 4, 8, 14},
++ .flags = UART_CAP_FIFO | UART_CAP_AFE,
++ },
+ };
+
+ /* Uart divisor latch read */
+@@ -1143,6 +1151,11 @@ static void autoconfig_16550a(struct uar
+ up->port.type = PORT_U6_16550A;
+ up->capabilities |= UART_CAP_AFE;
+ }
++
++ if ((up->port.type == PORT_16550A) && (up->probe & UART_PROBE_AFE)) {
++ up->port.type = PORT_16550A_AFE;
++ up->capabilities |= UART_CAP_AFE;
++ }
+ }
+
+ /*
+@@ -2813,7 +2826,6 @@ serial8250_do_set_termios(struct uart_po
+ if (termios->c_cflag & CRTSCTS)
+ up->mcr |= UART_MCR_AFE;
+ }
+-
+ /*
+ * Update the per-port timeout.
+ */
+--- a/include/linux/serial_8250.h
++++ b/include/linux/serial_8250.h
+@@ -141,6 +141,7 @@ struct uart_8250_port {
+ unsigned char probe;
+ struct mctrl_gpios *gpios;
+ #define UART_PROBE_RSA (1 << 0)
++#define UART_PROBE_AFE (1 << 1)
+
+ /*
+ * Some bits in registers are cleared on a read, so they must
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -245,4 +245,6 @@
+ /* Sunplus UART */
+ #define PORT_SUNPLUS 123
+
++#define PORT_16550A_AFE 124
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */
--- /dev/null
+From 6edee93a89254f30c3387c88231e7ecec06ba84a Mon Sep 17 00:00:00 2001
+From: "shanlong.li" <shanlong.li@starfivetech.com>
+Date: Mon, 10 Jul 2023 03:07:57 -0700
+Subject: [PATCH 067/116] driver:uart: fix up uart communicate fail
+
+fix up uart communicate fail
+
+Signed-off-by: shanlong.li <shanlong.li@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_dw.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -652,10 +652,10 @@ static int dw8250_probe(struct platform_
+ if (err)
+ return err;
+
+- data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+- if (IS_ERR(data->rst))
+- return PTR_ERR(data->rst);
+-
++ data->rst = devm_reset_control_array_get_exclusive(dev);
++ if (IS_ERR(data->rst)) {
++ err = PTR_ERR(data->rst);
++ }
+ reset_control_deassert(data->rst);
+
+ err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst);
--- /dev/null
+From 777d288f03a0b350f6c2d4367b01a80d9f25cd6e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Wed, 20 Sep 2023 17:19:59 +0800
+Subject: [PATCH 068/116] uart: 8250: add reset operation in runtime PM
+
+add reset operation in runtime PM
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_dw.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -745,6 +745,8 @@ static int dw8250_runtime_suspend(struct
+ {
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
++ reset_control_assert(data->rst);
++
+ clk_disable_unprepare(data->clk);
+
+ clk_disable_unprepare(data->pclk);
+@@ -760,6 +762,8 @@ static int dw8250_runtime_resume(struct
+
+ clk_prepare_enable(data->clk);
+
++ reset_control_deassert(data->rst);
++
+ return 0;
+ }
+
--- /dev/null
+From 5eda2331a252436756fb40861f01a7a38b1502c7 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Thu, 15 Jun 2023 20:14:22 +0800
+Subject: [PATCH 069/116] dt-bindings: CAN: Add StarFive CAN module
+
+Add documentation to describe StarFive CAN engine.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ .../devicetree/bindings/net/can/ipms-can.yaml | 97 +++++++++++++++++++
+ 1 file changed, 97 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/can/ipms-can.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/can/ipms-can.yaml
+@@ -0,0 +1,97 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++#$id: http://devicetree.org/schemas/net/can/ipms-can.yaml#
++#$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IPMS CAN/CANFD controller Device Tree Bindings
++
++properties:
++ compatible:
++ const:ipms,can
++
++ reg:
++ maxItems: 1
++ items:
++ - description:CAN controller registers
++
++ interrupts:
++ maxItems: 1
++
++ clocks:
++ minItems: 1
++ items:
++ - description:apb_clk clock
++ - description:core_clk clock
++ - description:timer_clk clock
++
++ clock-names:
++ minItems: 1
++ items:
++ - const:apb_clk
++ - const:core_clk
++ - const:timer_clk
++ resets:
++ minItems: 1
++ items:
++ - description:apb_clk reset
++ - description:core_clk reset
++ - description:timer_clk reset
++ reset-names:
++ minItems: 1
++ items:
++ - const:rst_apb
++ - const:rst_core
++ - const:rst_timer
++ starfive,sys-syscon:
++ format:
++ starfive,sys-syscon = <&arg0 arg1 arg2 arg3>
++ description:
++ arg0:arg0 is sys_syscon.
++ arg1:arg1 is syscon register offset, used to enable can2.0/canfd function, can0 is 0x10, can1 is 0x88.
++ arg2:arg2 is used to enable the register shift of the can2.0/canfd function, can0 is 0x3, can1 is 0x12.
++ arg3:arg3 is used to enable the register mask of the can2.0/canfd function, can0 is 0x8, can1 is 0x40000
++
++ syscon,can_or_canfd:
++ description:
++ IPMS CAN-CTRL core is a serial communications controller that performs serial communication according to the CAN protocol.
++ This CAN bus interface uses the basic CAN principle and meets all constraints of the CAN-specification 2.0B active.
++ Furthermore this CAN core can be configured to meet the specification of CAN with flexible data rate CAN FD.
++ When syscon,can_or_canfd is set to 0, use CAN2.0B.
++ when syscon,can_or_canfd is set to 1, use CAN FD.
++required:
++ - compatible
++ - reg
++ - interrupts
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
++ - starfive,sys-syscon
++ - syscon,can_or_canfd
++additionalProperties:false
++
++examples:
++ - |
++ can0: can@130d0000{
++ compatible = "ipms,can";
++ reg = <0x0 0x130d0000 0x0 0x1000>;
++ interrupts = <112>;
++ interrupt-parent = <&plic>;
++ clocks = <&clkgen JH7110_CAN0_CTRL_CLK_APB>,
++ <&clkgen JH7110_CAN0_CTRL_CLK_CAN>,
++ <&clkgen JH7110_CAN0_CTRL_CLK_TIMER>;
++ clock-names = "apb_clk",
++ "core_clk",
++ "timer_clk";
++ resets = <&rstgen RSTN_U0_CAN_CTRL_APB>,
++ <&rstgen RSTN_U0_CAN_CTRL_CORE>,
++ <&rstgen RSTN_U0_CAN_CTRL_TIMER>;
++ reset-names = "rst_apb",
++ "rst_core",
++ "rst_timer";
++ starfive,sys-syscon = <&sys_syscon, 0x10 0x3 0x8>;
++ syscon,can_or_canfd = <0>;
++ };
++
++...
--- /dev/null
+From b1fbe15b87be654b1b280a76ec1470917d79f720 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Thu, 15 Jun 2023 20:15:25 +0800
+Subject: [PATCH 070/116] CAN: starfive - Add CAN engine support
+
+Adding device probe StarFive CAN module.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/net/can/Kconfig | 5 +
+ drivers/net/can/Makefile | 1 +
+ drivers/net/can/ipms_canfd.c | 1275 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 1281 insertions(+)
+ create mode 100644 drivers/net/can/ipms_canfd.c
+
+--- a/drivers/net/can/Kconfig
++++ b/drivers/net/can/Kconfig
+@@ -214,6 +214,11 @@ config CAN_XILINXCAN
+ Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+ Zynq CANPS IP.
+
++config IPMS_CAN
++ tristate "IPMS CAN"
++ help
++ IPMS CANFD driver. This driver supports IPMS CANFD IP.
++
+ source "drivers/net/can/c_can/Kconfig"
+ source "drivers/net/can/cc770/Kconfig"
+ source "drivers/net/can/ctucanfd/Kconfig"
+--- a/drivers/net/can/Makefile
++++ b/drivers/net/can/Makefile
+@@ -31,5 +31,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/
+ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
+ obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
+ obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
++obj-$(CONFIG_IPMS_CAN) += ipms_canfd.o
+
+ subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
+--- /dev/null
++++ b/drivers/net/can/ipms_canfd.c
+@@ -0,0 +1,1275 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * StarFive Controller Area Network Host Controller Driver
++ *
++ * Copyright (c) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/can/dev.h>
++#include <linux/can/error.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regmap.h>
++
++#define DRIVER_NAME "ipms_canfd"
++
++/* CAN registers set */
++enum canfd_device_reg {
++ CANFD_RUBF_OFFSET = 0x00, /* Receive Buffer Registers 0x00-0x4f */
++ CANFD_RUBF_ID_OFFSET = 0x00,
++ CANFD_RBUF_CTL_OFFSET = 0x04,
++ CANFD_RBUF_DATA_OFFSET = 0x08,
++ CANFD_TBUF_OFFSET = 0x50, /* Transmit Buffer Registers 0x50-0x97 */
++ CANFD_TBUF_ID_OFFSET = 0x50,
++ CANFD_TBUF_CTL_OFFSET = 0x54,
++ CANFD_TBUF_DATA_OFFSET = 0x58,
++ CANFD_TTS_OFFSET = 0x98, /* Transmission Time Stamp 0x98-0x9f */
++ CANFD_CFG_STAT_OFFSET = 0xa0,
++ CANFD_TCMD_OFFSET = 0xa1,
++ CANFD_TCTRL_OFFSET = 0xa2,
++ CANFD_RCTRL_OFFSET = 0xa3,
++ CANFD_RTIE_OFFSET = 0xa4,
++ CANFD_RTIF_OFFSET = 0xa5,
++ CANFD_ERRINT_OFFSET = 0xa6,
++ CANFD_LIMIT_OFFSET = 0xa7,
++ CANFD_S_SEG_1_OFFSET = 0xa8,
++ CANFD_S_SEG_2_OFFSET = 0xa9,
++ CANFD_S_SJW_OFFSET = 0xaa,
++ CANFD_S_PRESC_OFFSET = 0xab,
++ CANFD_F_SEG_1_OFFSET = 0xac,
++ CANFD_F_SEG_2_OFFSET = 0xad,
++ CANFD_F_SJW_OFFSET = 0xae,
++ CANFD_F_PRESC_OFFSET = 0xaf,
++ CANFD_EALCAP_OFFSET = 0xb0,
++ CANFD_RECNT_OFFSET = 0xb2,
++ CANFD_TECNT_OFFSET = 0xb3,
++};
++
++enum canfd_reg_bitchange {
++ CAN_FD_SET_RST_MASK = 0x80, /* Set Reset Bit */
++ CAN_FD_OFF_RST_MASK = 0x7f, /* Reset Off Bit */
++ CAN_FD_SET_FULLCAN_MASK = 0x10, /* set TTTBM as 1->full TTCAN mode */
++ CAN_FD_OFF_FULLCAN_MASK = 0xef, /* set TTTBM as 0->separate PTB and STB mode */
++ CAN_FD_SET_FIFO_MASK = 0x20, /* set TSMODE as 1->FIFO mode */
++ CAN_FD_OFF_FIFO_MASK = 0xdf, /* set TSMODE as 0->Priority mode */
++ CAN_FD_SET_TSONE_MASK = 0x04,
++ CAN_FD_OFF_TSONE_MASK = 0xfb,
++ CAN_FD_SET_TSALL_MASK = 0x02,
++ CAN_FD_OFF_TSALL_MASK = 0xfd,
++ CAN_FD_LBMEMOD_MASK = 0x40, /* set loop back mode, external */
++ CAN_FD_LBMIMOD_MASK = 0x20, /* set loopback internal mode */
++ CAN_FD_SET_BUSOFF_MASK = 0x01,
++ CAN_FD_OFF_BUSOFF_MASK = 0xfe,
++ CAN_FD_SET_TTSEN_MASK = 0x80, /* set ttsen, tts update enable */
++ CAN_FD_SET_BRS_MASK = 0x10, /* can fd Bit Rate Switch mask */
++ CAN_FD_OFF_BRS_MASK = 0xef,
++ CAN_FD_SET_EDL_MASK = 0x20, /* Extended Data Length */
++ CAN_FD_OFF_EDL_MASK = 0xdf,
++ CAN_FD_SET_DLC_MASK = 0x0f,
++ CAN_FD_SET_TENEXT_MASK = 0x40,
++ CAN_FD_SET_IDE_MASK = 0x80,
++ CAN_FD_OFF_IDE_MASK = 0x7f,
++ CAN_FD_SET_RTR_MASK = 0x40,
++ CAN_FD_OFF_RTR_MASK = 0xbf,
++ CAN_FD_INTR_ALL_MASK = 0xff, /* all interrupts enable mask */
++ CAN_FD_SET_RIE_MASK = 0x80,
++ CAN_FD_OFF_RIE_MASK = 0x7f,
++ CAN_FD_SET_RFIE_MASK = 0x20,
++ CAN_FD_OFF_RFIE_MASK = 0xdf,
++ CAN_FD_SET_RAFIE_MASK = 0x10,
++ CAN_FD_OFF_RAFIE_MASK = 0xef,
++ CAN_FD_SET_EIE_MASK = 0x02,
++ CAN_FD_OFF_EIE_MASK = 0xfd,
++ CAN_FD_TASCTIVE_MASK = 0x02,
++ CAN_FD_RASCTIVE_MASK = 0x04,
++ CAN_FD_SET_TBSEL_MASK = 0x80, /* message writen in STB */
++ CAN_FD_OFF_TBSEL_MASK = 0x7f, /* message writen in PTB */
++ CAN_FD_SET_STBY_MASK = 0x20,
++ CAN_FD_OFF_STBY_MASK = 0xdf,
++ CAN_FD_SET_TPE_MASK = 0x10, /* Transmit primary enable */
++ CAN_FD_SET_TPA_MASK = 0x08,
++ CAN_FD_SET_SACK_MASK = 0x80,
++ CAN_FD_SET_RREL_MASK = 0x10,
++ CAN_FD_RSTAT_NOT_EMPTY_MASK = 0x03,
++ CAN_FD_SET_RIF_MASK = 0x80,
++ CAN_FD_OFF_RIF_MASK = 0x7f,
++ CAN_FD_SET_RAFIF_MASK = 0x10,
++ CAN_FD_SET_RFIF_MASK = 0x20,
++ CAN_FD_SET_TPIF_MASK = 0x08, /* Transmission Primary Interrupt Flag */
++ CAN_FD_SET_TSIF_MASK = 0x04,
++ CAN_FD_SET_EIF_MASK = 0x02,
++ CAN_FD_SET_AIF_MASK = 0x01,
++ CAN_FD_SET_EWARN_MASK = 0x80,
++ CAN_FD_SET_EPASS_MASK = 0x40,
++ CAN_FD_SET_EPIE_MASK = 0x20,
++ CAN_FD_SET_EPIF_MASK = 0x10,
++ CAN_FD_SET_ALIE_MASK = 0x08,
++ CAN_FD_SET_ALIF_MASK = 0x04,
++ CAN_FD_SET_BEIE_MASK = 0x02,
++ CAN_FD_SET_BEIF_MASK = 0x01,
++ CAN_FD_OFF_EPIE_MASK = 0xdf,
++ CAN_FD_OFF_BEIE_MASK = 0xfd,
++ CAN_FD_SET_AFWL_MASK = 0x40,
++ CAN_FD_SET_EWL_MASK = 0x0b,
++ CAN_FD_SET_KOER_MASK = 0xe0,
++ CAN_FD_SET_BIT_ERROR_MASK = 0x20,
++ CAN_FD_SET_FORM_ERROR_MASK = 0x40,
++ CAN_FD_SET_STUFF_ERROR_MASK = 0x60,
++ CAN_FD_SET_ACK_ERROR_MASK = 0x80,
++ CAN_FD_SET_CRC_ERROR_MASK = 0xa0,
++ CAN_FD_SET_OTH_ERROR_MASK = 0xc0,
++};
++
++/* seg1,seg2,sjw,prescaler all have 8 bits */
++#define BITS_OF_BITTIMING_REG 8
++
++/* in can_bittiming strucure every field has 32 bits---->u32 */
++#define FBITS_IN_BITTIMING_STR 32
++#define SEG_1_SHIFT 0
++#define SEG_2_SHIFT 8
++#define SJW_SHIFT 16
++#define PRESC_SHIFT 24
++
++/* TTSEN bit used for 32 bit register read or write */
++#define TTSEN_8_32_SHIFT 24
++#define RTR_32_8_SHIFT 24
++
++/* transmit mode */
++#define XMIT_FULL 0
++#define XMIT_SEP_FIFO 1
++#define XMIT_SEP_PRIO 2
++#define XMIT_PTB_MODE 3
++
++enum IPMS_CAN_TYPE {
++ IPMS_CAN_TYPY_CAN = 0,
++ IPMS_CAN_TYPE_CANFD,
++};
++
++struct ipms_canfd_priv {
++ struct can_priv can;
++ struct napi_struct napi;
++ struct device *dev;
++ struct regmap *reg_syscon;
++ void __iomem *reg_base;
++ u32 (*read_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg);
++ void (*write_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg, u32 val);
++ struct clk *can_clk;
++ u32 tx_mode;
++ struct reset_control *resets;
++ struct clk_bulk_data *clks;
++ int nr_clks;
++ u32 can_or_canfd;
++};
++
++static struct can_bittiming_const canfd_bittiming_const = {
++ .name = DRIVER_NAME,
++ .tseg1_min = 2,
++ .tseg1_max = 16,
++ .tseg2_min = 2,
++ .tseg2_max = 8,
++ .sjw_max = 4,
++ .brp_min = 1,
++ .brp_max = 512,
++ .brp_inc = 1,
++
++};
++
++static struct can_bittiming_const canfd_data_bittiming_const = {
++ .name = DRIVER_NAME,
++ .tseg1_min = 1,
++ .tseg1_max = 16,
++ .tseg2_min = 2,
++ .tseg2_max = 8,
++ .sjw_max = 8,
++ .brp_min = 1,
++ .brp_max = 512,
++ .brp_inc = 1,
++};
++
++static void canfd_write_reg_le(const struct ipms_canfd_priv *priv,
++ enum canfd_device_reg reg, u32 val)
++{
++ iowrite32(val, priv->reg_base + reg);
++}
++
++static u32 canfd_read_reg_le(const struct ipms_canfd_priv *priv,
++ enum canfd_device_reg reg)
++{
++ return ioread32(priv->reg_base + reg);
++}
++
++static inline unsigned char can_ioread8(const void *addr)
++{
++ void *addr_down;
++ union val {
++ u8 val_8[4];
++ u32 val_32;
++ } val;
++ u32 offset = 0;
++
++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
++ offset = addr - addr_down;
++ val.val_32 = ioread32(addr_down);
++ return val.val_8[offset];
++}
++
++static inline void can_iowrite8(unsigned char value, void *addr)
++{
++ void *addr_down;
++ union val {
++ u8 val_8[4];
++ u32 val_32;
++ } val;
++ u8 offset = 0;
++
++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
++ offset = addr - addr_down;
++ val.val_32 = ioread32(addr_down);
++ val.val_8[offset] = value;
++ iowrite32(val.val_32, addr_down);
++}
++
++static void canfd_reigister_set_bit(const struct ipms_canfd_priv *priv,
++ enum canfd_device_reg reg,
++ enum canfd_reg_bitchange set_mask)
++{
++ void *addr_down;
++ union val {
++ u8 val_8[4];
++ u32 val_32;
++ } val;
++ u8 offset = 0;
++
++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
++ offset = (priv->reg_base + reg) - addr_down;
++ val.val_32 = ioread32(addr_down);
++ val.val_8[offset] |= set_mask;
++ iowrite32(val.val_32, addr_down);
++}
++
++static void canfd_reigister_off_bit(const struct ipms_canfd_priv *priv,
++ enum canfd_device_reg reg,
++ enum canfd_reg_bitchange set_mask)
++{
++ void *addr_down;
++ union val {
++ u8 val_8[4];
++ u32 val_32;
++ } val;
++ u8 offset = 0;
++
++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
++ offset = (priv->reg_base + reg) - addr_down;
++ val.val_32 = ioread32(addr_down);
++ val.val_8[offset] &= set_mask;
++ iowrite32(val.val_32, addr_down);
++}
++
++static int canfd_device_driver_bittime_configuration(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ struct can_bittiming *bt = &priv->can.bittiming;
++ struct can_bittiming *dbt = &priv->can.data_bittiming;
++ u32 reset_test, bittiming_temp, dat_bittiming;
++
++ reset_test = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++ if (!(reset_test & CAN_FD_SET_RST_MASK)) {
++ netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n");
++ return -EPERM;
++ }
++
++ bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
++ ((bt->phase_seg2 - 1) << SEG_2_SHIFT) |
++ ((bt->sjw - 1) << SJW_SHIFT) |
++ ((bt->brp - 1) << PRESC_SHIFT);
++
++ /* Check the bittime parameter */
++ if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) ||
++ (((int)(bt->phase_seg2) - 1) < 0) ||
++ (((int)(bt->sjw) - 1) < 0) ||
++ (((int)(bt->brp) - 1) < 0))
++ return -EINVAL;
++
++ priv->write_reg(priv, CANFD_S_SEG_1_OFFSET, bittiming_temp);
++
++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) {
++ dat_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
++ ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) |
++ ((dbt->sjw - 1) << SJW_SHIFT) |
++ ((dbt->brp - 1) << PRESC_SHIFT);
++
++ if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) ||
++ (((int)(dbt->phase_seg2) - 1) < 0) ||
++ (((int)(dbt->sjw) - 1) < 0) ||
++ (((int)(dbt->brp) - 1) < 0))
++ return -EINVAL;
++
++ priv->write_reg(priv, CANFD_F_SEG_1_OFFSET, dat_bittiming);
++ }
++
++ canfd_reigister_off_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_OFF_RST_MASK);
++
++ netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CANFD_S_SEG_1_OFFSET));
++ netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CANFD_F_SEG_1_OFFSET));
++
++ return 0;
++}
++
++int canfd_get_freebuffer(struct ipms_canfd_priv *priv)
++{
++ /* Get next transmit buffer */
++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_TENEXT_MASK);
++
++ if (can_ioread8(priv->reg_base + CANFD_TCTRL_OFFSET) & CAN_FD_SET_TENEXT_MASK)
++ return -1;
++
++ return 0;
++}
++
++static void canfd_tx_interrupt(struct net_device *ndev, u8 isr)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ /* wait till transmission of the PTB or STB finished */
++ while (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) {
++ if (isr & CAN_FD_SET_TPIF_MASK)
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TPIF_MASK);
++
++ if (isr & CAN_FD_SET_TSIF_MASK)
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TSIF_MASK);
++
++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET);
++ }
++ netif_wake_queue(ndev);
++}
++
++static int can_rx(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ struct net_device_stats *stats = &ndev->stats;
++ struct can_frame *cf;
++ struct sk_buff *skb;
++ u32 can_id;
++ u8 dlc, control, rx_status;
++
++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK))
++ return 0;
++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET);
++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK;
++
++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
++ if (!skb) {
++ stats->rx_dropped++;
++ return 0;
++ }
++ cf->can_dlc = can_cc_dlc2len(dlc);
++
++ /* change the CANFD id into socketcan id format */
++ if (control & CAN_FD_SET_IDE_MASK) {
++ cf->can_id = can_id;
++ cf->can_id |= CAN_EFF_FLAG;
++ } else {
++ cf->can_id = can_id;
++ cf->can_id &= (~CAN_EFF_FLAG);
++ }
++
++ if (control & CAN_FD_SET_RTR_MASK)
++ cf->can_id |= CAN_RTR_FLAG;
++
++ if (!(control & CAN_FD_SET_RTR_MASK)) {
++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET);
++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4);
++ }
++
++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK);
++ stats->rx_bytes += can_fd_dlc2len(cf->can_dlc);
++ stats->rx_packets++;
++ netif_receive_skb(skb);
++
++ return 1;
++}
++
++static int canfd_rx(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ struct net_device_stats *stats = &ndev->stats;
++ struct canfd_frame *cf;
++ struct sk_buff *skb;
++ u32 can_id;
++ u8 dlc, control, rx_status;
++ int i;
++
++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK))
++ return 0;
++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET);
++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK;
++
++ if (control & CAN_FD_SET_EDL_MASK)
++ /* allocate sk_buffer for canfd frame */
++ skb = alloc_canfd_skb(ndev, &cf);
++ else
++ /* allocate sk_buffer for can frame */
++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
++
++ if (!skb) {
++ stats->rx_dropped++;
++ return 0;
++ }
++
++ /* change the CANFD or CAN2.0 data into socketcan data format */
++ if (control & CAN_FD_SET_EDL_MASK)
++ cf->len = can_fd_dlc2len(dlc);
++ else
++ cf->len = can_cc_dlc2len(dlc);
++
++ /* change the CANFD id into socketcan id format */
++ if (control & CAN_FD_SET_EDL_MASK) {
++ cf->can_id = can_id;
++ if (control & CAN_FD_SET_IDE_MASK)
++ cf->can_id |= CAN_EFF_FLAG;
++ else
++ cf->can_id &= (~CAN_EFF_FLAG);
++ } else {
++ cf->can_id = can_id;
++ if (control & CAN_FD_SET_IDE_MASK)
++ cf->can_id |= CAN_EFF_FLAG;
++ else
++ cf->can_id &= (~CAN_EFF_FLAG);
++
++ if (control & CAN_FD_SET_RTR_MASK)
++ cf->can_id |= CAN_RTR_FLAG;
++ }
++
++ /* CANFD frames handed over to SKB */
++ if (control & CAN_FD_SET_EDL_MASK) {
++ for (i = 0; i < cf->len; i += 4)
++ *((u32 *)(cf->data + i)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + i);
++ } else {
++ /* skb reads the received datas, if the RTR bit not set */
++ if (!(control & CAN_FD_SET_RTR_MASK)) {
++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET);
++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4);
++ }
++ }
++
++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK);
++
++ stats->rx_bytes += cf->len;
++ stats->rx_packets++;
++ netif_receive_skb(skb);
++
++ return 1;
++}
++
++static int canfd_rx_poll(struct napi_struct *napi, int quota)
++{
++ struct net_device *ndev = napi->dev;
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ int work_done = 0;
++ u8 rx_status = 0, control = 0;
++
++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++ /* clear receive interrupt and deal with all the received frames */
++ while ((rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK) && (work_done < quota)) {
++ (control & CAN_FD_SET_EDL_MASK) ? (work_done += canfd_rx(ndev)) : (work_done += can_rx(ndev));
++
++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++ }
++ napi_complete(napi);
++ canfd_reigister_set_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_SET_RIE_MASK);
++ return work_done;
++}
++
++static void canfd_rxfull_interrupt(struct net_device *ndev, u8 isr)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ if (isr & CAN_FD_SET_RAFIF_MASK)
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RAFIF_MASK);
++
++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK))
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET,
++ (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK));
++}
++
++static int set_canfd_xmit_mode(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ switch (priv->tx_mode) {
++ case XMIT_FULL:
++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FULLCAN_MASK);
++ break;
++ case XMIT_SEP_FIFO:
++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK);
++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FIFO_MASK);
++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK);
++ break;
++ case XMIT_SEP_PRIO:
++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK);
++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FIFO_MASK);
++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK);
++ break;
++ case XMIT_PTB_MODE:
++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_TBSEL_MASK);
++ break;
++ default:
++ break;
++ }
++ return 0;
++}
++
++static netdev_tx_t canfd_driver_start_xmit(struct sk_buff *skb, struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
++ struct net_device_stats *stats = &ndev->stats;
++ u32 ttsen, id, ctl, addr_off;
++ int i;
++
++ priv->tx_mode = XMIT_PTB_MODE;
++
++ if (can_dropped_invalid_skb(ndev, skb))
++ return NETDEV_TX_OK;
++
++ switch (priv->tx_mode) {
++ case XMIT_FULL:
++ return NETDEV_TX_BUSY;
++ case XMIT_PTB_MODE:
++ set_canfd_xmit_mode(ndev);
++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_STBY_MASK);
++
++ if (cf->can_id & CAN_EFF_FLAG) {
++ id = (cf->can_id & CAN_EFF_MASK);
++ ttsen = 0 << TTSEN_8_32_SHIFT;
++ id |= ttsen;
++ } else {
++ id = (cf->can_id & CAN_SFF_MASK);
++ ttsen = 0 << TTSEN_8_32_SHIFT;
++ id |= ttsen;
++ }
++
++ ctl = can_fd_len2dlc(cf->len);
++
++ /* transmit can fd frame */
++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) {
++ if (can_is_canfd_skb(skb)) {
++ if (cf->can_id & CAN_EFF_FLAG)
++ ctl |= CAN_FD_SET_IDE_MASK;
++ else
++ ctl &= CAN_FD_OFF_IDE_MASK;
++
++ if (cf->flags & CANFD_BRS)
++ ctl |= CAN_FD_SET_BRS_MASK;
++
++ ctl |= CAN_FD_SET_EDL_MASK;
++
++ addr_off = CANFD_TBUF_DATA_OFFSET;
++
++ for (i = 0; i < cf->len; i += 4) {
++ priv->write_reg(priv, addr_off,
++ *((u32 *)(cf->data + i)));
++ addr_off += 4;
++ }
++ } else {
++ ctl &= CAN_FD_OFF_EDL_MASK;
++ ctl &= CAN_FD_OFF_BRS_MASK;
++
++ if (cf->can_id & CAN_EFF_FLAG)
++ ctl |= CAN_FD_SET_IDE_MASK;
++ else
++ ctl &= CAN_FD_OFF_IDE_MASK;
++
++ if (cf->can_id & CAN_RTR_FLAG) {
++ ctl |= CAN_FD_SET_RTR_MASK;
++ priv->write_reg(priv,
++ CANFD_TBUF_ID_OFFSET, id);
++ priv->write_reg(priv,
++ CANFD_TBUF_CTL_OFFSET, ctl);
++ } else {
++ ctl &= CAN_FD_OFF_RTR_MASK;
++ addr_off = CANFD_TBUF_DATA_OFFSET;
++ priv->write_reg(priv, addr_off,
++ *((u32 *)(cf->data + 0)));
++ priv->write_reg(priv, addr_off + 4,
++ *((u32 *)(cf->data + 4)));
++ }
++ }
++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++ addr_off = CANFD_TBUF_DATA_OFFSET;
++ } else {
++ ctl &= CAN_FD_OFF_EDL_MASK;
++ ctl &= CAN_FD_OFF_BRS_MASK;
++
++ if (cf->can_id & CAN_EFF_FLAG)
++ ctl |= CAN_FD_SET_IDE_MASK;
++ else
++ ctl &= CAN_FD_OFF_IDE_MASK;
++
++ if (cf->can_id & CAN_RTR_FLAG) {
++ ctl |= CAN_FD_SET_RTR_MASK;
++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++ } else {
++ ctl &= CAN_FD_OFF_RTR_MASK;
++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++ addr_off = CANFD_TBUF_DATA_OFFSET;
++ priv->write_reg(priv, addr_off,
++ *((u32 *)(cf->data + 0)));
++ priv->write_reg(priv, addr_off + 4,
++ *((u32 *)(cf->data + 4)));
++ }
++ }
++ canfd_reigister_set_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TPE_MASK);
++ stats->tx_bytes += cf->len;
++ break;
++ default:
++ break;
++ }
++
++ /*Due to cache blocking, we need call dev_kfree_skb() here to free the socket
++ buffer and return NETDEV_TX_OK */
++ dev_kfree_skb(skb);
++
++ return NETDEV_TX_OK;
++}
++
++static int set_reset_mode(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ u8 ret;
++
++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++ ret |= CAN_FD_SET_RST_MASK;
++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++ return 0;
++}
++
++static void canfd_driver_stop(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ int ret;
++
++ ret = set_reset_mode(ndev);
++ if (ret)
++ netdev_err(ndev, "Mode Resetting Failed!\n");
++
++ priv->can.state = CAN_STATE_STOPPED;
++}
++
++static int canfd_driver_close(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ netif_stop_queue(ndev);
++ napi_disable(&priv->napi);
++ canfd_driver_stop(ndev);
++
++ free_irq(ndev->irq, ndev);
++ close_candev(ndev);
++
++ pm_runtime_put(priv->dev);
++
++ return 0;
++}
++
++static enum can_state get_of_chip_status(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ u8 can_stat, eir;
++
++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET);
++
++ if (can_stat & CAN_FD_SET_BUSOFF_MASK)
++ return CAN_STATE_BUS_OFF;
++
++ if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK))
++ return CAN_STATE_ERROR_PASSIVE;
++
++ if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK))
++ return CAN_STATE_ERROR_WARNING;
++
++ if (~(eir & CAN_FD_SET_EPASS_MASK))
++ return CAN_STATE_ERROR_ACTIVE;
++
++ return CAN_STATE_ERROR_ACTIVE;
++}
++
++static void canfd_error_interrupt(struct net_device *ndev, u8 isr, u8 eir)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ struct net_device_stats *stats = &ndev->stats;
++ struct can_frame *cf;
++ struct sk_buff *skb;
++ u8 koer, recnt = 0, tecnt = 0, can_stat = 0;
++
++ skb = alloc_can_err_skb(ndev, &cf);
++
++ koer = can_ioread8(priv->reg_base + CANFD_EALCAP_OFFSET) & CAN_FD_SET_KOER_MASK;
++ recnt = can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET);
++ tecnt = can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET);
++
++ /*Read can status*/
++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++ /* Bus off --->active error mode */
++ if ((isr & CAN_FD_SET_EIF_MASK) && priv->can.state == CAN_STATE_BUS_OFF)
++ priv->can.state = get_of_chip_status(ndev);
++
++ /* State selection */
++ if (can_stat & CAN_FD_SET_BUSOFF_MASK) {
++ priv->can.state = get_of_chip_status(ndev);
++ priv->can.can_stats.bus_off++;
++ canfd_reigister_set_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_SET_BUSOFF_MASK);
++ can_bus_off(ndev);
++ if (skb)
++ cf->can_id |= CAN_ERR_BUSOFF;
++
++ } else if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) {
++ priv->can.state = get_of_chip_status(ndev);
++ priv->can.can_stats.error_passive++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_CRTL;
++ cf->data[1] |= (recnt > 127) ? CAN_ERR_CRTL_RX_PASSIVE : 0;
++ cf->data[1] |= (tecnt > 127) ? CAN_ERR_CRTL_TX_PASSIVE : 0;
++ cf->data[6] = tecnt;
++ cf->data[7] = recnt;
++ }
++ } else if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) {
++ priv->can.state = get_of_chip_status(ndev);
++ priv->can.can_stats.error_warning++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_CRTL;
++ cf->data[1] |= (recnt > 95) ? CAN_ERR_CRTL_RX_WARNING : 0;
++ cf->data[1] |= (tecnt > 95) ? CAN_ERR_CRTL_TX_WARNING : 0;
++ cf->data[6] = tecnt;
++ cf->data[7] = recnt;
++ }
++ }
++
++ /* Check for in protocol defined error interrupt */
++ if (eir & CAN_FD_SET_BEIF_MASK) {
++ if (skb)
++ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
++
++ /* bit error interrupt */
++ if (koer == CAN_FD_SET_BIT_ERROR_MASK) {
++ stats->tx_errors++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_PROT;
++ cf->data[2] = CAN_ERR_PROT_BIT;
++ }
++ }
++ /* format error interrupt */
++ if (koer == CAN_FD_SET_FORM_ERROR_MASK) {
++ stats->rx_errors++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_PROT;
++ cf->data[2] = CAN_ERR_PROT_FORM;
++ }
++ }
++ /* stuffing error interrupt */
++ if (koer == CAN_FD_SET_STUFF_ERROR_MASK) {
++ stats->rx_errors++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_PROT;
++ cf->data[3] = CAN_ERR_PROT_STUFF;
++ }
++ }
++ /* ack error interrupt */
++ if (koer == CAN_FD_SET_ACK_ERROR_MASK) {
++ stats->tx_errors++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_PROT;
++ cf->data[2] = CAN_ERR_PROT_LOC_ACK;
++ }
++ }
++ /* crc error interrupt */
++ if (koer == CAN_FD_SET_CRC_ERROR_MASK) {
++ stats->rx_errors++;
++ if (skb) {
++ cf->can_id |= CAN_ERR_PROT;
++ cf->data[2] = CAN_ERR_PROT_LOC_CRC_SEQ;
++ }
++ }
++ priv->can.can_stats.bus_error++;
++ }
++ if (skb) {
++ stats->rx_packets++;
++ stats->rx_bytes += cf->can_dlc;
++ netif_rx(skb);
++ }
++
++ netdev_dbg(ndev, "Recnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET));
++ netdev_dbg(ndev, "Tecnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET));
++}
++
++static irqreturn_t canfd_interrupt(int irq, void *dev_id)
++{
++ struct net_device *ndev = (struct net_device *)dev_id;
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ u8 isr, eir;
++ u8 isr_handled = 0, eir_handled = 0;
++
++ /* read the value of interrupt status register */
++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET);
++
++ /* read the value of error interrupt register */
++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET);
++
++ /* Check for Tx interrupt and Processing it */
++ if (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) {
++ canfd_tx_interrupt(ndev, isr);
++ isr_handled |= (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK);
++ }
++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) {
++ canfd_rxfull_interrupt(ndev, isr);
++ isr_handled |= (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK);
++ }
++ /* Check Rx interrupt and Processing the receive interrupt routine */
++ if (isr & CAN_FD_SET_RIF_MASK) {
++ canfd_reigister_off_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_OFF_RIE_MASK);
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RIF_MASK);
++
++ napi_schedule(&priv->napi);
++ isr_handled |= CAN_FD_SET_RIF_MASK;
++ }
++ if ((isr & CAN_FD_SET_EIF_MASK) | (eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK))) {
++ /* reset EPIF and BEIF. Reset EIF */
++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET,
++ eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK));
++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET,
++ isr & CAN_FD_SET_EIF_MASK);
++
++ canfd_error_interrupt(ndev, isr, eir);
++
++ isr_handled |= CAN_FD_SET_EIF_MASK;
++ eir_handled |= (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK);
++ }
++ if ((isr_handled == 0) && (eir_handled == 0)) {
++ netdev_err(ndev, "Unhandled interrupt!\n");
++ return IRQ_NONE;
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int canfd_chip_start(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ int err;
++ u8 ret;
++
++ err = set_reset_mode(ndev);
++ if (err) {
++ netdev_err(ndev, "Mode Resetting Failed!\n");
++ return err;
++ }
++
++ err = canfd_device_driver_bittime_configuration(ndev);
++ if (err) {
++ netdev_err(ndev, "Bittime Setting Failed!\n");
++ return err;
++ }
++
++ /* Set Almost Full Warning Limit */
++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_AFWL_MASK);
++
++ /* Programmable Error Warning Limit = (EWL+1)*8. Set EWL=11->Error Warning=96 */
++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_EWL_MASK);
++
++ /* Interrupts enable */
++ can_iowrite8(CAN_FD_INTR_ALL_MASK, priv->reg_base + CANFD_RTIE_OFFSET);
++
++ /* Error Interrupts enable(Error Passive and Bus Error) */
++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, CAN_FD_SET_EPIE_MASK);
++
++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++ /* Check whether it is loopback mode or normal mode */
++ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
++ ret |= CAN_FD_LBMIMOD_MASK;
++ } else {
++ ret &= ~CAN_FD_LBMEMOD_MASK;
++ ret &= ~CAN_FD_LBMIMOD_MASK;
++ }
++
++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++ priv->can.state = CAN_STATE_ERROR_ACTIVE;
++
++ return 0;
++}
++
++static int canfd_do_set_mode(struct net_device *ndev, enum can_mode mode)
++{
++ int ret;
++
++ switch (mode) {
++ case CAN_MODE_START:
++ ret = canfd_chip_start(ndev);
++ if (ret) {
++ netdev_err(ndev, "Could Not Start CAN device !!\n");
++ return ret;
++ }
++ netif_wake_queue(ndev);
++ break;
++ default:
++ ret = -EOPNOTSUPP;
++ break;
++ }
++
++ return ret;
++}
++
++static int canfd_driver_open(struct net_device *ndev)
++{
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ int ret;
++
++ ret = pm_runtime_get_sync(priv->dev);
++ if (ret < 0) {
++ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
++ __func__, ret);
++ goto err;
++ }
++
++ /* Set chip into reset mode */
++ ret = set_reset_mode(ndev);
++ if (ret) {
++ netdev_err(ndev, "Mode Resetting Failed!\n");
++ return ret;
++ }
++
++ /* Common open */
++ ret = open_candev(ndev);
++ if (ret)
++ return ret;
++
++ /* Register interrupt handler */
++ ret = request_irq(ndev->irq, canfd_interrupt, IRQF_SHARED, ndev->name, ndev);
++ if (ret) {
++ netdev_err(ndev, "Request_irq err: %d\n", ret);
++ goto exit_irq;
++ }
++
++ ret = canfd_chip_start(ndev);
++ if (ret) {
++ netdev_err(ndev, "Could Not Start CAN device !\n");
++ goto exit_can_start;
++ }
++
++ napi_enable(&priv->napi);
++ netif_start_queue(ndev);
++
++ return 0;
++
++exit_can_start:
++ free_irq(ndev->irq, ndev);
++err:
++ pm_runtime_put(priv->dev);
++exit_irq:
++ close_candev(ndev);
++ return ret;
++}
++
++static int canfd_control_parse_dt(struct ipms_canfd_priv *priv)
++{
++ struct of_phandle_args args;
++ u32 syscon_mask, syscon_shift;
++ u32 can_or_canfd;
++ u32 syscon_offset, regval;
++ int ret;
++
++ ret = of_parse_phandle_with_fixed_args(priv->dev->of_node,
++ "starfive,sys-syscon", 3, 0, &args);
++ if (ret) {
++ dev_err(priv->dev, "Failed to parse starfive,sys-syscon\n");
++ return -EINVAL;
++ }
++
++ priv->reg_syscon = syscon_node_to_regmap(args.np);
++ of_node_put(args.np);
++ if (IS_ERR(priv->reg_syscon))
++ return PTR_ERR(priv->reg_syscon);
++
++ syscon_offset = args.args[0];
++ syscon_shift = args.args[1];
++ syscon_mask = args.args[2];
++
++ ret = device_property_read_u32(priv->dev, "syscon,can_or_canfd", &can_or_canfd);
++ if (ret)
++ goto exit_parse;
++
++ priv->can_or_canfd = can_or_canfd;
++
++ /* enable can2.0/canfd function */
++ regval = can_or_canfd << syscon_shift;
++ ret = regmap_update_bits(priv->reg_syscon, syscon_offset, syscon_mask, regval);
++ if (ret)
++ return ret;
++ return 0;
++exit_parse:
++ return ret;
++}
++
++static const struct net_device_ops canfd_netdev_ops = {
++ .ndo_open = canfd_driver_open,
++ .ndo_stop = canfd_driver_close,
++ .ndo_start_xmit = canfd_driver_start_xmit,
++ .ndo_change_mtu = can_change_mtu,
++};
++
++static int canfd_driver_probe(struct platform_device *pdev)
++{
++ struct net_device *ndev;
++ struct ipms_canfd_priv *priv;
++ void __iomem *addr;
++ int ret;
++ u32 frq;
++
++ addr = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(addr)) {
++ ret = PTR_ERR(addr);
++ goto exit;
++ }
++
++ ndev = alloc_candev(sizeof(struct ipms_canfd_priv), 1);
++ if (!ndev) {
++ ret = -ENOMEM;
++ goto exit;
++ }
++
++ priv = netdev_priv(ndev);
++ priv->dev = &pdev->dev;
++
++ ret = canfd_control_parse_dt(priv);
++ if (ret)
++ goto free_exit;
++
++ priv->nr_clks = devm_clk_bulk_get_all(priv->dev, &priv->clks);
++ if (priv->nr_clks < 0) {
++ dev_err(priv->dev, "Failed to get can clocks\n");
++ ret = -ENODEV;
++ goto free_exit;
++ }
++
++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks);
++ if (ret) {
++ dev_err(priv->dev, "Failed to enable clocks\n");
++ goto free_exit;
++ }
++
++ priv->resets = devm_reset_control_array_get_exclusive(priv->dev);
++ if (IS_ERR(priv->resets)) {
++ ret = PTR_ERR(priv->resets);
++ dev_err(priv->dev, "Failed to get can resets");
++ goto clk_exit;
++ }
++
++ ret = reset_control_deassert(priv->resets);
++ if (ret)
++ goto clk_exit;
++ priv->can.bittiming_const = &canfd_bittiming_const;
++ priv->can.data_bittiming_const = &canfd_data_bittiming_const;
++ priv->can.do_set_mode = canfd_do_set_mode;
++
++ /* in user space the execution mode can be chosen */
++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD)
++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD;
++ else
++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK;
++ priv->reg_base = addr;
++ priv->write_reg = canfd_write_reg_le;
++ priv->read_reg = canfd_read_reg_le;
++
++ pm_runtime_enable(&pdev->dev);
++
++ priv->can_clk = devm_clk_get(&pdev->dev, "core_clk");
++ if (IS_ERR(priv->can_clk)) {
++ dev_err(&pdev->dev, "Device clock not found.\n");
++ ret = PTR_ERR(priv->can_clk);
++ goto reset_exit;
++ }
++
++ device_property_read_u32(priv->dev, "frequency", &frq);
++ clk_set_rate(priv->can_clk, frq);
++
++ priv->can.clock.freq = clk_get_rate(priv->can_clk);
++ ndev->irq = platform_get_irq(pdev, 0);
++
++ /* we support local echo */
++ ndev->flags |= IFF_ECHO;
++ ndev->netdev_ops = &canfd_netdev_ops;
++
++ platform_set_drvdata(pdev, ndev);
++ SET_NETDEV_DEV(ndev, &pdev->dev);
++
++ netif_napi_add(ndev, &priv->napi, canfd_rx_poll);
++ ret = register_candev(ndev);
++ if (ret) {
++ dev_err(&pdev->dev, "Fail to register failed (err=%d)\n", ret);
++ goto reset_exit;
++ }
++
++ dev_dbg(&pdev->dev, "Driver registered: regs=%p, irp=%d, clock=%d\n",
++ priv->reg_base, ndev->irq, priv->can.clock.freq);
++
++ return 0;
++
++reset_exit:
++ reset_control_assert(priv->resets);
++clk_exit:
++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++free_exit:
++ free_candev(ndev);
++exit:
++ return ret;
++}
++
++static int canfd_driver_remove(struct platform_device *pdev)
++{
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ reset_control_assert(priv->resets);
++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++ pm_runtime_disable(&pdev->dev);
++
++ unregister_candev(ndev);
++ netif_napi_del(&priv->napi);
++ free_candev(ndev);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int __maybe_unused canfd_suspend(struct device *dev)
++{
++ struct net_device *ndev = dev_get_drvdata(dev);
++
++ if (netif_running(ndev)) {
++ netif_stop_queue(ndev);
++ netif_device_detach(ndev);
++ canfd_driver_stop(ndev);
++ }
++
++ return pm_runtime_force_suspend(dev);
++}
++
++static int __maybe_unused canfd_resume(struct device *dev)
++{
++ struct net_device *ndev = dev_get_drvdata(dev);
++ int ret;
++
++ ret = pm_runtime_force_resume(dev);
++ if (ret) {
++ dev_err(dev, "pm_runtime_force_resume failed on resume\n");
++ return ret;
++ }
++
++ if (netif_running(ndev)) {
++ ret = canfd_chip_start(ndev);
++ if (ret) {
++ dev_err(dev, "canfd_chip_start failed on resume\n");
++ return ret;
++ }
++
++ netif_device_attach(ndev);
++ netif_start_queue(ndev);
++ }
++
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_PM
++static int canfd_runtime_suspend(struct device *dev)
++{
++ struct net_device *ndev = dev_get_drvdata(dev);
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++ reset_control_assert(priv->resets);
++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++
++ return 0;
++}
++
++static int canfd_runtime_resume(struct device *dev)
++{
++ struct net_device *ndev = dev_get_drvdata(dev);
++ struct ipms_canfd_priv *priv = netdev_priv(ndev);
++ int ret;
++
++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks);
++ if (ret) {
++ dev_err(dev, "Failed to prepare_enable clk\n");
++ return ret;
++ }
++
++ ret = reset_control_deassert(priv->resets);
++ if (ret) {
++ dev_err(dev, "Failed to deassert reset\n");
++ return ret;
++ }
++
++ return 0;
++}
++#endif
++
++static const struct dev_pm_ops canfd_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(canfd_suspend, canfd_resume)
++ SET_RUNTIME_PM_OPS(canfd_runtime_suspend,
++ canfd_runtime_resume, NULL)
++};
++
++static const struct of_device_id canfd_of_match[] = {
++ { .compatible = "ipms,can" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, canfd_of_match);
++
++static struct platform_driver can_driver = {
++ .probe = canfd_driver_probe,
++ .remove = canfd_driver_remove,
++ .driver = {
++ .name = DRIVER_NAME,
++ .pm = &canfd_pm_ops,
++ .of_match_table = canfd_of_match,
++ },
++};
++
++module_platform_driver(can_driver);
++
++MODULE_DESCRIPTION("ipms can controller driver for StarFive jh7110 SoC");
++MODULE_AUTHOR("William Qiu<william.qiu@starfivetech.com");
++MODULE_LICENSE("GPL");
--- /dev/null
+From b12213d474966fbf47e7afa36a6655b5b241cf36 Mon Sep 17 00:00:00 2001
+From: "Kevin.xie" <kevin.xie@starfivetech.com>
+Date: Fri, 9 Jun 2023 14:57:13 +0800
+Subject: [PATCH 071/116] regulator: starfive-jh7110: Add regulator support for
+ JH7110 A type EVB.
+
+Add 7 regulators base on regulator framework for
+JH7110 evb HW design.
+
+Signed-off-by: Kevin.xie <kevin.xie@starfivetech.com>
+---
+ drivers/regulator/Kconfig | 10 ++
+ drivers/regulator/Makefile | 1 +
+ drivers/regulator/starfive-jh7110-regulator.c | 126 ++++++++++++++++++
+ include/linux/regulator/jh7110.h | 24 ++++
+ 4 files changed, 161 insertions(+)
+ create mode 100644 drivers/regulator/starfive-jh7110-regulator.c
+ create mode 100644 include/linux/regulator/jh7110.h
+
+--- a/drivers/regulator/Kconfig
++++ b/drivers/regulator/Kconfig
+@@ -1335,6 +1335,16 @@ config REGULATOR_SM5703
+ This driver provides support for voltage regulators of SM5703
+ multi-function device.
+
++config REGULATOR_STARFIVE_JH7110
++ tristate "Starfive JH7110 PMIC"
++ depends on I2C
++ select REGMAP_I2C
++ help
++ Say y here to select this option to enable the power regulator of
++ Starfive JH7110 PMIC.
++ This driver supports the control of different power rails of device
++ through regulator interface.
++
+ config REGULATOR_STM32_BOOSTER
+ tristate "STMicroelectronics STM32 BOOSTER"
+ depends on ARCH_STM32 || COMPILE_TEST
+--- a/drivers/regulator/Makefile
++++ b/drivers/regulator/Makefile
+@@ -156,6 +156,7 @@ obj-$(CONFIG_REGULATOR_SC2731) += sc2731
+ obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+ obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o
+ obj-$(CONFIG_REGULATOR_SM5703) += sm5703-regulator.o
++obj-$(CONFIG_REGULATOR_STARFIVE_JH7110) += starfive-jh7110-regulator.o
+ obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o
+ obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+ obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
+--- /dev/null
++++ b/drivers/regulator/starfive-jh7110-regulator.c
+@@ -0,0 +1,126 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2022 Starfive Technology Co., Ltd.
++ * Author: Mason Huo <mason.huo@starfivetech.com>
++ */
++
++#include <linux/err.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/regulator/driver.h>
++#include <linux/regulator/machine.h>
++#include <linux/regulator/of_regulator.h>
++#include <linux/regulator/jh7110.h>
++#include <linux/slab.h>
++
++#define JH7110_PM_POWER_SW_0 0x80
++#define JH7110_PM_POWER_SW_1 0x81
++#define ENABLE_MASK(id) BIT(id)
++
++
++static const struct regmap_config jh7110_regmap_config = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ .max_register = JH7110_PM_POWER_SW_1,
++ .cache_type = REGCACHE_FLAT,
++};
++
++static const struct regulator_ops jh7110_ldo_ops = {
++ .enable = regulator_enable_regmap,
++ .disable = regulator_disable_regmap,
++ .is_enabled = regulator_is_enabled_regmap,
++};
++
++#define JH7110_LDO(_id, _name, en_reg, en_mask) \
++{\
++ .name = (_name),\
++ .ops = &jh7110_ldo_ops,\
++ .of_match = of_match_ptr(_name),\
++ .regulators_node = of_match_ptr("regulators"),\
++ .type = REGULATOR_VOLTAGE,\
++ .id = JH7110_ID_##_id,\
++ .owner = THIS_MODULE,\
++ .enable_reg = JH7110_PM_POWER_SW_##en_reg,\
++ .enable_mask = ENABLE_MASK(en_mask),\
++}
++
++static const struct regulator_desc jh7110_regulators[] = {
++ JH7110_LDO(LDO_REG1, "hdmi_1p8", 0, 0),
++ JH7110_LDO(LDO_REG2, "mipitx_1p8", 0, 1),
++ JH7110_LDO(LDO_REG3, "mipirx_1p8", 0, 2),
++ JH7110_LDO(LDO_REG4, "hdmi_0p9", 0, 3),
++ JH7110_LDO(LDO_REG5, "mipitx_0p9", 0, 4),
++ JH7110_LDO(LDO_REG6, "mipirx_0p9", 0, 5),
++ JH7110_LDO(LDO_REG7, "sdio_vdd", 1, 0),
++};
++
++static int jh7110_i2c_probe(struct i2c_client *i2c)
++{
++ struct regulator_config config = { };
++ struct regulator_dev *rdev;
++ struct regulator_init_data *init_data;
++ struct regmap *regmap;
++ int i, ret;
++
++ regmap = devm_regmap_init_i2c(i2c, &jh7110_regmap_config);
++ if (IS_ERR(regmap)) {
++ ret = PTR_ERR(regmap);
++ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
++ ret);
++ return ret;
++ }
++
++ init_data = of_get_regulator_init_data(&i2c->dev, i2c->dev.of_node, NULL);
++ if (!init_data)
++ return -ENOMEM;
++ config.init_data = init_data;
++
++ for (i = 0; i < JH7110_MAX_REGULATORS; i++) {
++ config.dev = &i2c->dev;
++ config.regmap = regmap;
++
++ rdev = devm_regulator_register(&i2c->dev,
++ &jh7110_regulators[i], &config);
++ if (IS_ERR(rdev)) {
++ dev_err(&i2c->dev,
++ "Failed to register JH7110 regulator\n");
++ return PTR_ERR(rdev);
++ }
++ }
++
++ return 0;
++}
++
++static const struct i2c_device_id jh7110_i2c_id[] = {
++ {"jh7110_evb_reg", 0},
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, jh7110_i2c_id);
++
++#ifdef CONFIG_OF
++static const struct of_device_id jh7110_dt_ids[] = {
++ { .compatible = "starfive,jh7110-evb-regulator",
++ .data = &jh7110_i2c_id[0] },
++ {},
++};
++MODULE_DEVICE_TABLE(of, jh7110_dt_ids);
++#endif
++
++static struct i2c_driver jh7110_regulator_driver = {
++ .driver = {
++ .name = "jh7110-evb-regulator",
++ .of_match_table = of_match_ptr(jh7110_dt_ids),
++ },
++ .probe = jh7110_i2c_probe,
++ .id_table = jh7110_i2c_id,
++};
++
++module_i2c_driver(jh7110_regulator_driver);
++
++MODULE_AUTHOR("Mason Huo <mason.huo@starfivetech.com>");
++MODULE_DESCRIPTION("Regulator device driver for Starfive JH7110");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/include/linux/regulator/jh7110.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2022 Starfive Technology Co., Ltd.
++ * Author: Mason Huo <mason.huo@starfivetech.com>
++ */
++
++#ifndef __LINUX_REGULATOR_JH7110_H
++#define __LINUX_REGULATOR_JH7110_H
++
++#define JH7110_MAX_REGULATORS 7
++
++
++enum jh7110_reg_id {
++ JH7110_ID_LDO_REG1 = 0,
++ JH7110_ID_LDO_REG2,
++ JH7110_ID_LDO_REG3,
++ JH7110_ID_LDO_REG4,
++ JH7110_ID_LDO_REG5,
++ JH7110_ID_LDO_REG6,
++ JH7110_ID_LDO_REG7,
++};
++
++
++#endif /* __LINUX_REGULATOR_JH7110_H */
--- /dev/null
+From f0b4cffe4d1813305f783d208f260747ecc56c50 Mon Sep 17 00:00:00 2001
+From: "Kevin.xie" <kevin.xie@starfivetech.com>
+Date: Thu, 24 Nov 2022 16:59:12 +0800
+Subject: [PATCH 072/116] drivers: nvme: Add precheck and delay for CQE pending
+ status.
+
+To workaroud the NVMe I/O timeout problem in bootup S10udev case
+which caused by the CQE update lantancy.
+
+Signed-off-by: Kevin.xie <kevin.xie@starfivetech.com>
+---
+ drivers/nvme/host/pci.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -28,6 +28,7 @@
+ #include <linux/io-64-nonatomic-hi-lo.h>
+ #include <linux/sed-opal.h>
+ #include <linux/pci-p2pdma.h>
++#include <linux/delay.h>
+
+ #include "trace.h"
+ #include "nvme.h"
+@@ -1062,6 +1063,15 @@ static inline int nvme_poll_cq(struct nv
+ {
+ int found = 0;
+
++ /*
++ * In some cases, such as udev trigger, cqe status may update
++ * a little bit later than MSI, which cause an irq handle missing.
++ * To workaound, here we will prefetch the status first, and wait
++ * 1us if we get nothing.
++ */
++ if (!nvme_cqe_pending(nvmeq))
++ udelay(1);
++
+ while (nvme_cqe_pending(nvmeq)) {
+ found++;
+ /*
--- /dev/null
+From eb294df4b9fab46bc5dbf676edf51e28e06d1968 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:09 +0000
+Subject: [PATCH 073/116] RISC-V: Create unique identification for SoC PMU
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The SBI PMU platform driver did not provide any identification for
+perf events matching. This patch introduces a new sysfs file inside the
+platform device (soc:pmu/id) for pmu identification.
+
+The identification is a 64-bit value generated as:
+[63-32]: mvendorid;
+[31]: marchid[MSB];
+[30-16]: marchid[15-0];
+[15-0]: mimpid[15MSBs];
+
+The CSRs are detailed in the RISC-V privileged spec [1].
+The marchid is split in MSB + 15LSBs, due to the MSB being used for
+open-source architecture identification.
+
+[1] https://github.com/riscv/riscv-isa-manual
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+---
+ drivers/perf/riscv_pmu_sbi.c | 47 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/drivers/perf/riscv_pmu_sbi.c
++++ b/drivers/perf/riscv_pmu_sbi.c
+@@ -1019,6 +1019,46 @@ static struct ctl_table sbi_pmu_sysctl_t
+ { }
+ };
+
++static uint64_t pmu_sbi_get_pmu_id(void)
++{
++ union sbi_pmu_id {
++ uint64_t value;
++ struct {
++ uint16_t imp:16;
++ uint16_t arch:16;
++ uint32_t vendor:32;
++ };
++ } pmuid;
++
++ pmuid.value = 0;
++ pmuid.vendor = (uint32_t) sbi_get_mvendorid();
++ pmuid.arch = (sbi_get_marchid() >> (63 - 15) & (1 << 15)) | (sbi_get_marchid() & 0x7FFF);
++ pmuid.imp = (sbi_get_mimpid() >> 16);
++
++ return pmuid.value;
++}
++
++static ssize_t pmu_sbi_id_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ int len;
++
++ len = sprintf(buf, "0x%llx\n", pmu_sbi_get_pmu_id());
++ if (len <= 0)
++ dev_err(dev, "mydrv: Invalid sprintf len: %dn", len);
++
++ return len;
++}
++
++static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, pmu_sbi_id_show, 0);
++
++static struct attribute *pmu_sbi_attrs[] = {
++ &dev_attr_id.attr,
++ NULL
++};
++
++ATTRIBUTE_GROUPS(pmu_sbi);
++
+ static int pmu_sbi_device_probe(struct platform_device *pdev)
+ {
+ struct riscv_pmu *pmu = NULL;
+@@ -1067,6 +1107,13 @@ static int pmu_sbi_device_probe(struct p
+ pmu->event_unmapped = pmu_sbi_event_unmapped;
+ pmu->csr_index = pmu_sbi_csr_index;
+
++ ret = sysfs_create_group(&pdev->dev.kobj, &pmu_sbi_group);
++ if (ret) {
++ dev_err(&pdev->dev, "sysfs creation failed\n");
++ return ret;
++ }
++ pdev->dev.groups = pmu_sbi_groups;
++
+ ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+ if (ret)
+ return ret;
--- /dev/null
+From 1dc069ffadf4ce7817a716f9df2f480254e9b01d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:10 +0000
+Subject: [PATCH 074/116] RISC-V: Support CPUID for risc-v in perf
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch creates the header.c file for the risc-v architecture and introduces support for
+PMU identification through sysfs.
+It is now possible to configure pmu-events in risc-v.
+
+Depends on patch [1], that introduces the id sysfs file.
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+Signed-off-by: minda.chen <minda.chen@starfivetech.com>
+---
+ drivers/perf/riscv_pmu.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/perf/riscv_pmu.c
++++ b/drivers/perf/riscv_pmu.c
+@@ -18,6 +18,23 @@
+
+ #include <asm/sbi.h>
+
++PMU_FORMAT_ATTR(event, "config:0-63");
++
++static struct attribute *riscv_arch_formats_attr[] = {
++ &format_attr_event.attr,
++ NULL,
++};
++
++static struct attribute_group riscv_pmu_format_group = {
++ .name = "format",
++ .attrs = riscv_arch_formats_attr,
++};
++
++static const struct attribute_group *riscv_pmu_attr_groups[] = {
++ &riscv_pmu_format_group,
++ NULL,
++};
++
+ static bool riscv_perf_user_access(struct perf_event *event)
+ {
+ return ((event->attr.type == PERF_TYPE_HARDWARE) ||
+@@ -410,6 +427,7 @@ struct riscv_pmu *riscv_pmu_alloc(void)
+ cpuc->events[i] = NULL;
+ }
+ pmu->pmu = (struct pmu) {
++ .attr_groups = riscv_pmu_attr_groups,
+ .event_init = riscv_pmu_event_init,
+ .event_mapped = riscv_pmu_event_mapped,
+ .event_unmapped = riscv_pmu_event_unmapped,
--- /dev/null
+From 3e6ea12dda276c01a756764fcafa315b19860c33 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:11 +0000
+Subject: [PATCH 075/116] RISC-V: Added generic pmu-events mapfile
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The pmu-events now supports custom events for RISC-V, plus the cycle,
+time and instret events were defined.
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+---
+ .../pmu-events/arch/riscv/riscv-generic.json | 20 +++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+ create mode 100644 tools/perf/pmu-events/arch/riscv/riscv-generic.json
+
+--- /dev/null
++++ b/tools/perf/pmu-events/arch/riscv/riscv-generic.json
+@@ -0,0 +1,20 @@
++[
++ {
++ "PublicDescription": "CPU Cycles",
++ "EventCode": "0x00",
++ "EventName": "riscv_cycles",
++ "BriefDescription": "CPU cycles RISC-V generic counter"
++ },
++ {
++ "PublicDescription": "CPU Time",
++ "EventCode": "0x01",
++ "EventName": "riscv_time",
++ "BriefDescription": "CPU time RISC-V generic counter"
++ },
++ {
++ "PublicDescription": "CPU Instructions",
++ "EventCode": "0x02",
++ "EventName": "riscv_instret",
++ "BriefDescription": "CPU retired instructions RISC-V generic counter"
++ }
++]
+\ No newline at end of file
--- /dev/null
+From 30e0cdcf9e05faa65ecde4ed8b70039568fdb660 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Thu, 2 Mar 2023 17:16:01 +0800
+Subject: [PATCH 076/116] perf: sbi: disable cpu hotplug callback.
+
+register cpu hotplug callback will cause dhrystone
+and coremark benchmark reduce the scores. this CPU
+hotplug ops will do in sbi cpu/on and off. So disable
+this no side effect.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/perf/riscv_pmu_sbi.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/perf/riscv_pmu_sbi.c
++++ b/drivers/perf/riscv_pmu_sbi.c
+@@ -1114,9 +1114,11 @@ static int pmu_sbi_device_probe(struct p
+ }
+ pdev->dev.groups = pmu_sbi_groups;
+
++#ifndef CONFIG_ARCH_STARFIVE
+ ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+ if (ret)
+ return ret;
++#endif
+
+ ret = riscv_pm_pmu_register(pmu);
+ if (ret)
--- /dev/null
+From fc4b5c7c27e1b56b1f848e50511c4fd081b1b6c5 Mon Sep 17 00:00:00 2001
+From: Walker Chen <walker.chen@starfivetech.com>
+Date: Mon, 12 Jun 2023 21:21:45 +0800
+Subject: [PATCH 077/116] dmaengine: dw-axi-dmac: Drop unused print message
+
+Removed printing information which is not related to StarFive
+platform.
+
+Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -523,7 +523,7 @@ static void dw_axi_dma_set_hw_channel(st
+ unsigned long reg_value, val;
+
+ if (!chip->apb_regs) {
+- dev_err(chip->dev, "apb_regs not initialized\n");
++ dev_dbg(chip->dev, "apb_regs not initialized\n");
+ return;
+ }
+
--- /dev/null
+From cd2254c6be9441ebacaa35693ecb5ce116b90622 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Fri, 16 Jun 2023 16:27:46 +0800
+Subject: [PATCH 079/116] ASoC: codecs: Add AC108 Codec driver
+
+Add AC108 Codec driver and AC101 driver for AC10x.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/codecs/Kconfig | 5 +
+ sound/soc/codecs/Makefile | 2 +
+ sound/soc/codecs/ac101.c | 1716 +++++++++++++++++++++++++++++++++
+ sound/soc/codecs/ac101_regs.h | 431 +++++++++
+ sound/soc/codecs/ac108.c | 1622 +++++++++++++++++++++++++++++++
+ sound/soc/codecs/ac108.h | 749 ++++++++++++++
+ sound/soc/codecs/ac10x.h | 152 +++
+ 7 files changed, 4677 insertions(+)
+ create mode 100644 sound/soc/codecs/ac101.c
+ create mode 100644 sound/soc/codecs/ac101_regs.h
+ create mode 100644 sound/soc/codecs/ac108.c
+ create mode 100644 sound/soc/codecs/ac108.h
+ create mode 100644 sound/soc/codecs/ac10x.h
+
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS
+ depends on COMPILE_TEST
+ imply SND_SOC_88PM860X
+ imply SND_SOC_AB8500_CODEC
++ imply SND_SOC_AC108
+ imply SND_SOC_AC97_CODEC
+ imply SND_SOC_AD1836
+ imply SND_SOC_AD193X_SPI
+@@ -397,6 +398,10 @@ config SND_SOC_AB8500_CODEC
+ tristate
+ depends on ABX500_CORE
+
++config SND_SOC_AC108
++ tristate "AC108"
++ depends on I2C
++
+ config SND_SOC_AC97_CODEC
+ tristate "Build generic ASoC AC97 CODEC driver"
+ select SND_AC97_CODEC
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -2,6 +2,7 @@
+ snd-soc-88pm860x-objs := 88pm860x-codec.o
+ snd-soc-ab8500-codec-objs := ab8500-codec.o
+ snd-soc-ac97-objs := ac97.o
++snd-soc-ac108-objs := ac108.o ac101.o
+ snd-soc-ad1836-objs := ad1836.o
+ snd-soc-ad193x-objs := ad193x.o
+ snd-soc-ad193x-spi-objs := ad193x-spi.o
+@@ -386,6 +387,7 @@ snd-soc-simple-mux-objs := simple-mux.o
+
+ obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
+ obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
++obj-$(CONFIG_SND_SOC_AC108) += snd-soc-ac108.o
+ obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
+ obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
+--- /dev/null
++++ b/sound/soc/codecs/ac101.c
+@@ -0,0 +1,1716 @@
++/*
++ * ac101.c
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2014-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ *
++ * huangxin <huangxin@Reuuimllatech.com>
++ * liushaohua <liushaohua@allwinnertech.com>
++ *
++ * X-Powers AC101 codec driver
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/input.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <sound/tlv.h>
++#include <linux/workqueue.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "ac101_regs.h"
++#include "ac10x.h"
++
++/* #undef AC101_DEBG
++ * use 'make DEBUG=1' to enable debugging
++ */
++
++/*
++ * *** To sync channels ***
++ *
++ * 1. disable clock in codec hw_params()
++ * 2. clear fifo in bcm2835 hw_params()
++ * 3. clear fifo in bcm2385 prepare()
++ * 4. enable RX in bcm2835 trigger()
++ * 5. enable clock in machine trigger()
++ */
++
++/*Default initialize configuration*/
++static bool speaker_double_used = 1;
++static int double_speaker_val = 0x1B;
++static int single_speaker_val = 0x19;
++static int headset_val = 0x3B;
++static int mainmic_val = 0x4;
++static int headsetmic_val = 0x4;
++static bool dmic_used = 0;
++static int adc_digital_val = 0xb0b0;
++static bool drc_used = false;
++
++#define AC101_RATES (SNDRV_PCM_RATE_8000_96000 & \
++ ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
++ SNDRV_PCM_RATE_88200))
++#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \
++ SNDRV_PCM_FMTBIT_S24_LE |*/ \
++ SNDRV_PCM_FMTBIT_S32_LE | \
++ 0)
++
++static struct ac10x_priv* static_ac10x;
++
++
++int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int r, v = 0;
++
++ if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) {
++ dev_err(codec->dev, "read reg %02X fail\n",
++ reg);
++ return r;
++ }
++ return v;
++}
++
++int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int v;
++
++ v = regmap_write(ac10x->regmap101, reg, val);
++ return v;
++}
++
++int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg,
++ unsigned mask, unsigned value
++) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int v;
++
++ v = regmap_update_bits(ac10x->regmap101, reg, mask, value);
++ return v;
++}
++
++
++
++#ifdef CONFIG_AC101_SWITCH_DETECT
++/******************************************************************************/
++/********************************switch****************************************/
++/******************************************************************************/
++#define KEY_HEADSETHOOK 226 /* key define */
++#define HEADSET_FILTER_CNT (10)
++
++/*
++ * switch_hw_config:config the 53 codec register
++ */
++static void switch_hw_config(struct snd_soc_codec *codec)
++{
++ int r;
++
++ AC101_DBG();
++
++ /*HMIC/MMIC BIAS voltage level select:2.5v*/
++ ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<<BIASVOLTAGE), (0xf<<BIASVOLTAGE));
++ /*debounce when Key down or keyup*/
++ ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_M), (0x0<<HMIC_M));
++ /*debounce when earphone plugin or pullout*/
++ ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_N), (0x0<<HMIC_N));
++ /*Down Sample Setting Select: Downby 4,32Hz*/
++ ac101_update_bits(codec, HMIC_CTRL2, (0x3<<HMIC_SAMPLE_SELECT),
++ (0x02<<HMIC_SAMPLE_SELECT));
++ /*Hmic_th2 for detecting Keydown or Keyup.*/
++ ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH2), (0x8<<HMIC_TH2));
++ /*Hmic_th1[4:0],detecting eraphone plugin or pullout*/
++ ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH1), (0x1<<HMIC_TH1));
++ /*Headset microphone BIAS working mode: when HBIASEN = 1 */
++ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASMOD), (0x1<<HBIASMOD));
++ /*Headset microphone BIAS Enable*/
++ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0x1<<HBIASEN));
++ /*Headset microphone BIAS Current sensor & ADC Enable*/
++ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0x1<<HBIASADCEN));
++ /*Earphone Plugin/out Irq Enable*/
++ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PULLOUT_IRQ), (0x1<<HMIC_PULLOUT_IRQ));
++ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PLUGIN_IRQ), (0x1<<HMIC_PLUGIN_IRQ));
++
++ /*Hmic KeyUp/key down Irq Enable*/
++ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYDOWN_IRQ), (0x1<<HMIC_KEYDOWN_IRQ));
++ ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYUP_IRQ), (0x1<<HMIC_KEYUP_IRQ));
++
++ /*headphone calibration clock frequency select*/
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
++
++ /*clear hmic interrupt */
++ r = HMIC_PEND_ALL;
++ ac101_write(codec, HMIC_STS, r);
++
++ return;
++}
++
++/*
++ * switch_status_update: update the switch state.
++ */
++static void switch_status_update(struct ac10x_priv *ac10x)
++{
++ AC101_DBG("ac10x->state:%d\n", ac10x->state);
++
++ input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state);
++ input_sync(ac10x->inpdev);
++ return;
++}
++
++/*
++ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt.
++ */
++static void work_cb_clear_irq(struct work_struct *work)
++{
++ int reg_val = 0;
++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq);
++ struct snd_soc_codec *codec = ac10x->codec;
++
++ ac10x->irq_cntr++;
++
++ reg_val = ac101_read(codec, HMIC_STS);
++ if (BIT(HMIC_PULLOUT_PEND) & reg_val) {
++ ac10x->pullout_cntr++;
++ AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr);
++ }
++
++ reg_val |= HMIC_PEND_ALL;
++ ac101_write(codec, HMIC_STS, reg_val);
++
++ reg_val = ac101_read(codec, HMIC_STS);
++ if ((reg_val & HMIC_PEND_ALL) != 0){
++ reg_val |= HMIC_PEND_ALL;
++ ac101_write(codec, HMIC_STS, reg_val);
++ }
++
++ if (cancel_work_sync(&ac10x->work_switch) != 0) {
++ ac10x->irq_cntr--;
++ }
++
++ if (0 == schedule_work(&ac10x->work_switch)) {
++ ac10x->irq_cntr--;
++ AC101_DBG("[work_cb_clear_irq] add work struct failed!\n");
++ }
++}
++
++enum {
++ HBIAS_LEVEL_1 = 0x02,
++ HBIAS_LEVEL_2 = 0x0B,
++ HBIAS_LEVEL_3 = 0x13,
++ HBIAS_LEVEL_4 = 0x17,
++ HBIAS_LEVEL_5 = 0x19,
++};
++
++static int __ac101_get_hmic_data(struct snd_soc_codec *codec) {
++ #ifdef AC101_DEBG
++ static long counter;
++ #endif
++ int r, d;
++
++ d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS));
++
++ r = 0x1 << HMIC_DATA_PEND;
++ ac101_write(codec, HMIC_STS, r);
++
++ /* prevent i2c accessing too frequently */
++ usleep_range(1500, 3000);
++
++ AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d);
++ return d;
++}
++
++/*
++ * work_cb_earphone_switch: judge the status of the headphone
++ */
++static void work_cb_earphone_switch(struct work_struct *work)
++{
++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch);
++ struct snd_soc_codec *codec = ac10x->codec;
++
++ static int hook_flag1 = 0, hook_flag2 = 0;
++ static int KEY_VOLUME_FLAG = 0;
++
++ unsigned filter_buf = 0;
++ int filt_index = 0;
++ int t = 0;
++
++ ac10x->irq_cntr--;
++
++ /* read HMIC_DATA */
++ t = __ac101_get_hmic_data(codec);
++
++ if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
++ t = __ac101_get_hmic_data(codec);
++
++ if (t >= HBIAS_LEVEL_5){
++ msleep(150);
++ t = __ac101_get_hmic_data(codec);
++ if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5)
++ && (ac10x->pullout_cntr == 0)) {
++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1);
++ input_sync(ac10x->inpdev);
++
++ AC101_DBG("KEY_HEADSETHOOK1\n");
++
++ if (hook_flag1 != hook_flag2)
++ hook_flag1 = hook_flag2 = 0;
++ hook_flag1++;
++ }
++ if (ac10x->pullout_cntr)
++ ac10x->pullout_cntr--;
++ } else if (t >= HBIAS_LEVEL_4) {
++ msleep(80);
++ t = __ac101_get_hmic_data(codec);
++ if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) {
++ KEY_VOLUME_FLAG = 1;
++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1);
++ input_sync(ac10x->inpdev);
++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0);
++ input_sync(ac10x->inpdev);
++
++ AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t);
++ }
++ if (ac10x->pullout_cntr)
++ ac10x->pullout_cntr--;
++ } else if (t >= HBIAS_LEVEL_3){
++ msleep(80);
++ t = __ac101_get_hmic_data(codec);
++ if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3 && (ac10x->pullout_cntr == 0)) {
++ KEY_VOLUME_FLAG = 1;
++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1);
++ input_sync(ac10x->inpdev);
++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0);
++ input_sync(ac10x->inpdev);
++ AC101_DBG("KEY_VOLUMEDOWN\n");
++ }
++ if (ac10x->pullout_cntr)
++ ac10x->pullout_cntr--;
++ }
++ } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) &&
++ (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
++ t = __ac101_get_hmic_data(codec);
++ if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) {
++ if (KEY_VOLUME_FLAG) {
++ KEY_VOLUME_FLAG = 0;
++ }
++ if (hook_flag1 == (++hook_flag2)) {
++ hook_flag1 = hook_flag2 = 0;
++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0);
++ input_sync(ac10x->inpdev);
++
++ AC101_DBG("KEY_HEADSETHOOK0\n");
++ }
++ }
++ } else {
++ while (ac10x->irq_cntr == 0 && ac10x->irq != 0) {
++ msleep(20);
++
++ t = __ac101_get_hmic_data(codec);
++
++ if (filt_index <= HEADSET_FILTER_CNT) {
++ if (filt_index++ == 0) {
++ filter_buf = t;
++ } else if (filter_buf != t) {
++ filt_index = 0;
++ }
++ continue;
++ }
++
++ filt_index = 0;
++ if (filter_buf >= HBIAS_LEVEL_2) {
++ ac10x->mode = THREE_HEADPHONE_PLUGIN;
++ ac10x->state = 2;
++ } else if (filter_buf >= HBIAS_LEVEL_1 - 1) {
++ ac10x->mode = FOUR_HEADPHONE_PLUGIN;
++ ac10x->state = 1;
++ } else {
++ ac10x->mode = HEADPHONE_IDLE;
++ ac10x->state = 0;
++ }
++ switch_status_update(ac10x);
++ ac10x->pullout_cntr = 0;
++ break;
++ }
++ }
++}
++
++/*
++ * audio_hmic_irq: the interrupt handlers
++ */
++static irqreturn_t audio_hmic_irq(int irq, void *para)
++{
++ struct ac10x_priv *ac10x = (struct ac10x_priv *)para;
++ if (ac10x == NULL) {
++ return -EINVAL;
++ }
++
++ if (0 == schedule_work(&ac10x->work_clear_irq)){
++ AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n");
++ }
++ return IRQ_HANDLED;
++}
++
++static int ac101_switch_probe(struct ac10x_priv *ac10x) {
++ struct i2c_client *i2c = ac10x->i2c101;
++ long ret;
++
++ ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN);
++ if (IS_ERR(ac10x->gpiod_irq)) {
++ ac10x->gpiod_irq = NULL;
++ dev_err(&i2c->dev, "failed get switch-irq in device tree\n");
++ goto _err_irq;
++ }
++
++ gpiod_direction_input(ac10x->gpiod_irq);
++
++ ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq);
++ if (IS_ERR_VALUE(ac10x->irq)) {
++ pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq);
++ ac10x->irq = 0;
++ goto _err_irq;
++ }
++
++ /* request irq, set irq type to falling edge trigger */
++ ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq,
++ IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x);
++ if (IS_ERR_VALUE(ret)) {
++ pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret);
++ goto _err_irq;
++ }
++
++ ac10x->mode = HEADPHONE_IDLE;
++ ac10x->state = -1;
++
++ /*use for judge the state of switch*/
++ INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch);
++ INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq);
++
++ /********************create input device************************/
++ ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev);
++ if (!ac10x->inpdev) {
++ AC101_DBG("input_allocate_device: not enough memory for input device\n");
++ ret = -ENOMEM;
++ goto _err_input_allocate_device;
++ }
++
++ ac10x->inpdev->name = "seed-voicecard-headset";
++ ac10x->inpdev->phys = dev_name(ac10x->codec->dev);
++ ac10x->inpdev->id.bustype = BUS_I2C;
++ ac10x->inpdev->dev.parent = ac10x->codec->dev;
++ input_set_drvdata(ac10x->inpdev, ac10x->codec);
++
++ ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW);
++
++ set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit);
++ set_bit(KEY_VOLUMEUP, ac10x->inpdev->keybit);
++ set_bit(KEY_VOLUMEDOWN, ac10x->inpdev->keybit);
++ input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT);
++
++ ret = input_register_device(ac10x->inpdev);
++ if (ret) {
++ AC101_DBG("input_register_device: input_register_device failed\n");
++ goto _err_input_register_device;
++ }
++
++ /* the first headset state checking */
++ switch_hw_config(ac10x->codec);
++ ac10x->irq_cntr = 1;
++ schedule_work(&ac10x->work_switch);
++
++ return 0;
++
++_err_input_register_device:
++_err_input_allocate_device:
++
++ if (ac10x->irq) {
++ devm_free_irq(&i2c->dev, ac10x->irq, ac10x);
++ ac10x->irq = 0;
++ }
++_err_irq:
++ return ret;
++}
++/******************************************************************************/
++/********************************switch****************************************/
++/******************************************************************************/
++#endif
++
++
++
++void drc_config(struct snd_soc_codec *codec)
++{
++ int reg_val;
++ reg_val = ac101_read(codec, 0xa3);
++ reg_val &= ~(0x7ff<<0);
++ reg_val |= 1<<0;
++ ac101_write(codec, 0xa3, reg_val);
++ ac101_write(codec, 0xa4, 0x2baf);
++
++ reg_val = ac101_read(codec, 0xa5);
++ reg_val &= ~(0x7ff<<0);
++ reg_val |= 1<<0;
++ ac101_write(codec, 0xa5, reg_val);
++ ac101_write(codec, 0xa6, 0x2baf);
++
++ reg_val = ac101_read(codec, 0xa7);
++ reg_val &= ~(0x7ff<<0);
++ ac101_write(codec, 0xa7, reg_val);
++ ac101_write(codec, 0xa8, 0x44a);
++
++ reg_val = ac101_read(codec, 0xa9);
++ reg_val &= ~(0x7ff<<0);
++ ac101_write(codec, 0xa9, reg_val);
++ ac101_write(codec, 0xaa, 0x1e06);
++
++ reg_val = ac101_read(codec, 0xab);
++ reg_val &= ~(0x7ff<<0);
++ reg_val |= (0x352<<0);
++ ac101_write(codec, 0xab, reg_val);
++ ac101_write(codec, 0xac, 0x6910);
++
++ reg_val = ac101_read(codec, 0xad);
++ reg_val &= ~(0x7ff<<0);
++ reg_val |= (0x77a<<0);
++ ac101_write(codec, 0xad, reg_val);
++ ac101_write(codec, 0xae, 0xaaaa);
++
++ reg_val = ac101_read(codec, 0xaf);
++ reg_val &= ~(0x7ff<<0);
++ reg_val |= (0x2de<<0);
++ ac101_write(codec, 0xaf, reg_val);
++ ac101_write(codec, 0xb0, 0xc982);
++
++ ac101_write(codec, 0x16, 0x9f9f);
++
++}
++
++void drc_enable(struct snd_soc_codec *codec,bool on)
++{
++ int reg_val;
++ if (on) {
++ ac101_write(codec, 0xb5, 0xA080);
++ reg_val = ac101_read(codec, MOD_CLK_ENA);
++ reg_val |= (0x1<<6);
++ ac101_write(codec, MOD_CLK_ENA, reg_val);
++ reg_val = ac101_read(codec, MOD_RST_CTRL);
++ reg_val |= (0x1<<6);
++ ac101_write(codec, MOD_RST_CTRL, reg_val);
++
++ reg_val = ac101_read(codec, 0xa0);
++ reg_val |= (0x7<<0);
++ ac101_write(codec, 0xa0, reg_val);
++ } else {
++ ac101_write(codec, 0xb5, 0x0);
++ reg_val = ac101_read(codec, MOD_CLK_ENA);
++ reg_val &= ~(0x1<<6);
++ ac101_write(codec, MOD_CLK_ENA, reg_val);
++ reg_val = ac101_read(codec, MOD_RST_CTRL);
++ reg_val &= ~(0x1<<6);
++ ac101_write(codec, MOD_RST_CTRL, reg_val);
++
++ reg_val = ac101_read(codec, 0xa0);
++ reg_val &= ~(0x7<<0);
++ ac101_write(codec, 0xa0, reg_val);
++ }
++}
++
++void set_configuration(struct snd_soc_codec *codec)
++{
++ if (speaker_double_used) {
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
++ (double_speaker_val<<SPK_VOL));
++ } else {
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
++ (single_speaker_val<<SPK_VOL));
++ }
++ ac101_update_bits(codec, HPOUT_CTRL, (0x3f<<HP_VOL), (headset_val<<HP_VOL));
++ ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC1G), (mainmic_val<<ADC_MIC1G));
++ ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC2G), (headsetmic_val<<ADC_MIC2G));
++ if (dmic_used) {
++ ac101_write(codec, ADC_VOL_CTRL, adc_digital_val);
++ }
++ if (drc_used) {
++ drc_config(codec);
++ }
++ /*headphone calibration clock frequency select*/
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
++
++ /* I2S1 DAC Timeslot 0 data <- I2S1 DAC channel 0 */
++ // "AIF1IN0L Mux" <= "AIF1DACL"
++ // "AIF1IN0R Mux" <= "AIF1DACR"
++ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0L_SRC, 0x0 << AIF1_DA0L_SRC);
++ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_SRC, 0x0 << AIF1_DA0R_SRC);
++ /* Timeslot 0 Left & Right Channel enable */
++ ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_ENA, 0x3 << AIF1_DA0R_ENA);
++
++ /* DAC Digital Mixer Source Select <- I2S1 DA0 */
++ // "DACL Mixer" += "AIF1IN0L Mux"
++ // "DACR Mixer" += "AIF1IN0R Mux"
++ ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACL_MXR_ADCL, 0x8 << DACL_MXR_ADCL);
++ ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACR_MXR_ADCR, 0x8 << DACR_MXR_ADCR);
++ /* Internal DAC Analog Left & Right Channel enable */
++ ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << DACALEN, 0x3 << DACALEN);
++
++ /* Output Mixer Source Select */
++ // "Left Output Mixer" += "DACL Mixer"
++ // "Right Output Mixer" += "DACR Mixer"
++ ac101_update_bits(codec, OMIXER_SR, 0x1 << LMIXMUTEDACL, 0x1 << LMIXMUTEDACL);
++ ac101_update_bits(codec, OMIXER_SR, 0x1 << RMIXMUTEDACR, 0x1 << RMIXMUTEDACR);
++ /* Left & Right Analog Output Mixer enable */
++ ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << LMIXEN, 0x3 << LMIXEN);
++
++ /* Headphone Ouput Control */
++ // "HP_R Mux" <= "DACR Mixer"
++ // "HP_L Mux" <= "DACL Mixer"
++ ac101_update_bits(codec, HPOUT_CTRL, 0x1 << LHPS, 0x0 << LHPS);
++ ac101_update_bits(codec, HPOUT_CTRL, 0x1 << RHPS, 0x0 << RHPS);
++
++ /* Speaker Output Control */
++ // "SPK_L Mux" <= "SPK_LR Adder"
++ // "SPK_R Mux" <= "SPK_LR Adder"
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPKS) | (0x1 << RSPKS),
++ (0x1 << LSPKS) | (0x1 << RSPKS));
++ /* Enable Left & Right Speaker */
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++ (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
++ return;
++}
++
++static int late_enable_dac(struct snd_soc_codec* codec, int event) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ mutex_lock(&ac10x->dac_mutex);
++ switch (event) {
++ case SND_SOC_DAPM_PRE_PMU:
++ AC101_DBG();
++ if (ac10x->dac_enable == 0){
++ /*enable dac module clk*/
++ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
++ (0x1<<MOD_CLK_DAC_DIG));
++ ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
++ (0x1<<MOD_RESET_DAC_DIG));
++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x1<<ENDA));
++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x1<<ENHPF));
++ }
++ ac10x->dac_enable++;
++ break;
++ case SND_SOC_DAPM_POST_PMD:
++ if (ac10x->dac_enable != 0){
++ ac10x->dac_enable = 0;
++
++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x0<<ENHPF));
++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x0<<ENDA));
++ /*disable dac module clk*/
++ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
++ (0x0<<MOD_CLK_DAC_DIG));
++ ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
++ (0x0<<MOD_RESET_DAC_DIG));
++ }
++ break;
++ }
++ mutex_unlock(&ac10x->dac_mutex);
++ return 0;
++}
++
++static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
++ switch (event) {
++ case SND_SOC_DAPM_POST_PMU:
++ /*open*/
++ AC101_DBG("post:open\n");
++ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++ (0xf<<HPOUTPUTENABLE));
++ msleep(10);
++ ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x1<<HPPA_EN));
++ ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x3<<LHPPA_MUTE));
++ break;
++ case SND_SOC_DAPM_PRE_PMD:
++ /*close*/
++ AC101_DBG("pre:close\n");
++ ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x0<<LHPPA_MUTE));
++ msleep(10);
++ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++ (0x0<<HPOUTPUTENABLE));
++ ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x0<<HPPA_EN));
++ break;
++ }
++ return 0;
++}
++
++static int ac101_sysclk_started(void) {
++ int reg_val;
++
++ reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL);
++ return (reg_val & (0x1<<SYSCLK_ENA));
++}
++
++static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int ret = 0;
++
++ switch (event) {
++ case SND_SOC_DAPM_PRE_PMU:
++ if (ac10x->aif1_clken == 0){
++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
++ (0x1<<AIF1CLK_ENA));
++ if(!quick || _MASTER_MULTI_CODEC != _MASTER_AC101) {
++ /* enable aif1clk & sysclk */
++ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
++ (0x1<<MOD_CLK_AIF1),
++ (0x1<<MOD_CLK_AIF1));
++ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL,
++ (0x1<<MOD_RESET_AIF1),
++ (0x1<<MOD_RESET_AIF1));
++ }
++ ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
++ (0x1<<SYSCLK_ENA));
++
++ if (ret) {
++ AC101_DBG("start sysclk failed\n");
++ } else {
++ AC101_DBG("hw sysclk enable\n");
++ ac10x->aif1_clken++;
++ }
++ }
++ break;
++ case SND_SOC_DAPM_POST_PMD:
++ if (ac10x->aif1_clken != 0) {
++ /* disable aif1clk & sysclk */
++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
++ (0x0<<AIF1CLK_ENA));
++ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1),
++ (0x0<<MOD_CLK_AIF1));
++ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
++ (0x0<<MOD_RESET_AIF1));
++ ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
++ (0x0<<SYSCLK_ENA));
++
++ if (ret) {
++ AC101_DBG("stop sysclk failed\n");
++ } else {
++ AC101_DBG("hw sysclk disable\n");
++ ac10x->aif1_clken = 0;
++ }
++ break;
++ }
++ }
++
++ AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU,
++ SND_SOC_DAPM_POST_PMD);
++
++ return ret;
++}
++
++/**
++ * snd_ac101_get_volsw - single mixer get callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to get the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol
++){
++ struct soc_mixer_control *mc =
++ (struct soc_mixer_control *)kcontrol->private_value;
++ unsigned int val, mask = (1 << fls(mc->max)) - 1;
++ unsigned int invert = mc->invert;
++ int ret;
++
++ if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0)
++ return ret;
++
++ val = (ret >> mc->shift) & mask;
++ ucontrol->value.integer.value[0] = val - mc->min;
++ if (invert) {
++ ucontrol->value.integer.value[0] =
++ mc->max - ucontrol->value.integer.value[0];
++ }
++
++ if (snd_soc_volsw_is_stereo(mc)) {
++ val = (ret >> mc->rshift) & mask;
++ ucontrol->value.integer.value[1] = val - mc->min;
++ if (invert) {
++ ucontrol->value.integer.value[1] =
++ mc->max - ucontrol->value.integer.value[1];
++ }
++ }
++ return 0;
++}
++
++/**
++ * snd_ac101_put_volsw - single mixer put callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to set the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol
++){
++ struct soc_mixer_control *mc =
++ (struct soc_mixer_control *)kcontrol->private_value;
++ unsigned int sign_bit = mc->sign_bit;
++ unsigned int val, mask = (1 << fls(mc->max)) - 1;
++ unsigned int invert = mc->invert;
++ int ret;
++
++ if (sign_bit)
++ mask = BIT(sign_bit + 1) - 1;
++
++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
++ if (invert) {
++ val = mc->max - val;
++ }
++
++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift);
++
++ if (! snd_soc_volsw_is_stereo(mc)) {
++ return ret;
++ }
++ val = ((ucontrol->value.integer.value[1] + mc->min) & mask);
++ if (invert) {
++ val = mc->max - val;
++ }
++
++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift,
++ val << mc->rshift);
++ return ret;
++}
++
++
++static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0);
++static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0);
++static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0);
++static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0);
++static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0);
++
++static struct snd_kcontrol_new ac101_controls[] = {
++ /*DAC*/
++ SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv),
++ SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN,
++ 0xf, 0, dac_mix_vol_tlv),
++ SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv),
++ SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
++ SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
++};
++
++/* PLL divisors */
++struct pll_div {
++ unsigned int pll_in;
++ unsigned int pll_out;
++ int m;
++ int n_i;
++ int n_f;
++};
++
++struct aif1_fs {
++ unsigned samp_rate;
++ int bclk_div;
++ int srbit;
++ #define _SERIES_24_576K 0
++ #define _SERIES_22_579K 1
++ int series;
++};
++
++struct kv_map {
++ int val;
++ int bit;
++};
++
++/*
++ * Note : pll code from original tdm/i2s driver.
++ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2;
++ * N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0]
++ */
++static const struct pll_div codec_pll_div[] = {
++ {128000, _FREQ_22_579K, 1, 529, 1},
++ {192000, _FREQ_22_579K, 1, 352, 4},
++ {256000, _FREQ_22_579K, 1, 264, 3},
++ {384000, _FREQ_22_579K, 1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/
++ {1411200, _FREQ_22_579K, 1, 48, 0},
++ {2822400, _FREQ_22_579K, 1, 24, 0}, /* accurate, 11025 * 256 */
++ {5644800, _FREQ_22_579K, 1, 12, 0}, /* accurate, 22050 * 256 */
++ {6000000, _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/
++ {11289600, _FREQ_22_579K, 1, 6, 0}, /* accurate, 44100 * 256 */
++ {13000000, _FREQ_22_579K, 19, 99, 0},
++ {19200000, _FREQ_22_579K, 25, 88, 1},
++ {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */
++
++ {128000, _FREQ_24_576K, 1, 576, 0},
++ {192000, _FREQ_24_576K, 1, 384, 0},
++ {256000, _FREQ_24_576K, 1, 288, 0},
++ {384000, _FREQ_24_576K, 1, 192, 0},
++ {2048000, _FREQ_24_576K, 1, 36, 0}, /* accurate, 8000 * 256 */
++ {3072000, _FREQ_24_576K, 1, 24, 0}, /* accurate, 12000 * 256 */
++ {4096000, _FREQ_24_576K, 1, 18, 0}, /* accurate, 16000 * 256 */
++ {6000000, _FREQ_24_576K, 25, 307, 1},
++ {6144000, _FREQ_24_576K, 4, 48, 0}, /* accurate, 24000 * 256 */
++ {12288000, _FREQ_24_576K, 8, 48, 0}, /* accurate, 48000 * 256 */
++ {13000000, _FREQ_24_576K, 42, 238, 1},
++ {19200000, _FREQ_24_576K, 25, 96, 0},
++ {24000000, _FREQ_24_576K, 25, 76, 4}, /* accurate */
++
++ {_FREQ_22_579K, _FREQ_22_579K, 8, 24, 0}, /* accurate, 88200 * 256 */
++ {_FREQ_24_576K, _FREQ_24_576K, 8, 24, 0}, /* accurate, 96000 * 256 */
++};
++
++static const struct aif1_fs codec_aif1_fs[] = {
++ {8000, 12, 0},
++ {11025, 8, 1, _SERIES_22_579K},
++ {12000, 8, 2},
++ {16000, 6, 3},
++ {22050, 4, 4, _SERIES_22_579K},
++ {24000, 4, 5},
++ /* {32000, 3, 6}, dividing by 3 is not support */
++ {44100, 2, 7, _SERIES_22_579K},
++ {48000, 2, 8},
++ {96000, 1, 9},
++};
++
++static const struct kv_map codec_aif1_lrck[] = {
++ {16, 0},
++ {32, 1},
++ {64, 2},
++ {128, 3},
++ {256, 4},
++};
++
++static const struct kv_map codec_aif1_wsize[] = {
++ {8, 0},
++ {16, 1},
++ {20, 2},
++ {24, 3},
++ {32, 3},
++};
++
++static const unsigned ac101_bclkdivs[] = {
++ 1, 2, 4, 6,
++ 8, 12, 16, 24,
++ 32, 48, 64, 96,
++ 128, 192, 0, 0,
++};
++
++static int ac101_aif_play(struct ac10x_priv* ac10x) {
++ struct snd_soc_codec * codec = ac10x->codec;
++
++ late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU);
++ ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU);
++ if (drc_used) {
++ drc_enable(codec, 1);
++ }
++
++ /* Enable Left & Right Speaker */
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++ (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
++ if (ac10x->gpiod_spk_amp_gate) {
++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1);
++ }
++ return 0;
++}
++
++static void ac10x_work_aif_play(struct work_struct *work) {
++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work);
++
++ ac101_aif_play(ac10x);
++ return;
++}
++
++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ AC101_DBG("mute=%d\n", mute);
++
++ ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0);
++
++ if (!mute) {
++ #if _MASTER_MULTI_CODEC != _MASTER_AC101
++ /* enable global clock */
++ ac10x->aif1_clken = 0;
++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
++ ac101_aif_play(ac10x);
++ #else
++ schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50));
++ #endif
++ } else {
++ #if _MASTER_MULTI_CODEC == _MASTER_AC101
++ cancel_delayed_work_sync(&ac10x->dlywork);
++ #endif
++
++ if (ac10x->gpiod_spk_amp_gate) {
++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0);
++ }
++ /* Disable Left & Right Speaker */
++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++ (0x0 << LSPK_EN) | (0x0 << RSPK_EN));
++ if (drc_used) {
++ drc_enable(codec, 0);
++ }
++ ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD);
++ late_enable_dac(codec, SND_SOC_DAPM_POST_PMD);
++
++ #if _MASTER_MULTI_CODEC != _MASTER_AC101
++ ac10x->aif1_clken = 1;
++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++ #endif
++ }
++ return 0;
++}
++
++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n",
++ snd_pcm_stream_str(substream),
++ codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
++ codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active,
++ snd_soc_dai_active(codec_dai));
++
++ if (!snd_soc_dai_active(codec_dai)) {
++ ac10x->aif1_clken = 1;
++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++ } else {
++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
++ }
++}
++
++static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
++ unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ int i, m, n_i, n_f;
++
++ AC101_DBG("pll_id:%d\n", pll_id);
++
++ /* clear volatile reserved bits*/
++ ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0);
++
++ /* select aif1 clk srouce from mclk1 */
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x0<<AIF1CLK_SRC));
++ /* disable pll */
++ ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (0<<PLL_EN));
++
++ if (!freq_out)
++ return 0;
++ if ((freq_in < 128000) || (freq_in > _FREQ_24_576K)) {
++ return -EINVAL;
++ } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) {
++ if (pll_id == AC101_MCLK1) {
++ /*select aif1 clk source from mclk1*/
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC),
++ (0x0<<AIF1CLK_SRC));
++ return 0;
++ }
++ }
++
++ switch (pll_id) {
++ case AC101_MCLK1:
++ /*pll source from MCLK1*/
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x0<<PLLCLK_SRC));
++ break;
++ case AC101_BCLK1:
++ /*pll source from BCLK1*/
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x2<<PLLCLK_SRC));
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */
++ for (i = m = n_i = n_f = 0; i < ARRAY_SIZE(codec_pll_div); i++) {
++ if ((codec_pll_div[i].pll_in == freq_in) &&
++ (codec_pll_div[i].pll_out == freq_out)) {
++ m = codec_pll_div[i].m;
++ n_i = codec_pll_div[i].n_i;
++ n_f = codec_pll_div[i].n_f;
++ break;
++ }
++ }
++ /* config pll m */
++ if (m == 64) m = 0;
++ ac101_update_bits(codec, PLL_CTRL1, (0x3f<<PLL_POSTDIV_M), (m<<PLL_POSTDIV_M));
++ /* config pll n */
++ ac101_update_bits(codec, PLL_CTRL2, (0x3ff<<PLL_PREDIV_NI), (n_i<<PLL_PREDIV_NI));
++ ac101_update_bits(codec, PLL_CTRL2, (0x7<<PLL_POSTDIV_NF), (n_f<<PLL_POSTDIV_NF));
++ /* enable pll */
++ ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (1<<PLL_EN));
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<PLLCLK_ENA), (0x1<<PLLCLK_ENA));
++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x3<<AIF1CLK_SRC));
++
++ return 0;
++}
++
++int ac101_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *codec_dai)
++{
++ int i = 0;
++ int AIF_CLK_CTRL = AIF1_CLK_CTRL;
++ int aif1_word_size = 24;
++ int aif1_slot_size = 32;
++ int aif1_lrck_div;
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int reg_val, freq_out;
++ unsigned channels;
++
++ AC101_DBG("+++\n");
++
++ if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
++ /* not configure hw_param twice if stream is playback, tell the caller it's started */
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ return 1;
++ }
++ }
++
++ /* get channels count & slot size */
++ channels = params_channels(params);
++
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S24_LE:
++ case SNDRV_PCM_FORMAT_S32_LE:
++ aif1_slot_size = 32;
++ break;
++ case SNDRV_PCM_FORMAT_S16_LE:
++ default:
++ aif1_slot_size = 16;
++ break;
++ }
++
++ /* set LRCK/BCLK ratio */
++ aif1_lrck_div = aif1_slot_size * channels;
++ for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) {
++ if (codec_aif1_lrck[i].val == aif1_lrck_div) {
++ break;
++ }
++ }
++ ac101_update_bits(codec, AIF_CLK_CTRL, (0x7 << AIF1_LRCK_DIV),
++ codec_aif1_lrck[i].bit << AIF1_LRCK_DIV);
++
++ /* set PLL output freq */
++ freq_out = _FREQ_24_576K;
++ for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
++ if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
++ if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used &&
++ codec_aif1_fs[i].samp_rate == 44100) {
++ ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
++ (0x4<<AIF1_FS));
++ } else {
++ ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
++ ((codec_aif1_fs[i].srbit)<<AIF1_FS));
++ }
++ if (codec_aif1_fs[i].series == _SERIES_22_579K)
++ freq_out = _FREQ_22_579K;
++ break;
++ }
++ }
++
++ /* set I2S word size */
++ for (i = 0; i < ARRAY_SIZE(codec_aif1_wsize); i++) {
++ if (codec_aif1_wsize[i].val == aif1_word_size) {
++ break;
++ }
++ }
++ ac101_update_bits(codec, AIF_CLK_CTRL, (0x3<<AIF1_WORK_SIZ),
++ ((codec_aif1_wsize[i].bit)<<AIF1_WORK_SIZ));
++
++ /* set TDM slot size */
++ if ((reg_val = codec_aif1_wsize[i].bit) > 2) reg_val = 2;
++ ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ);
++
++ /* setting pll if it's master mode */
++ reg_val = ac101_read(codec, AIF_CLK_CTRL);
++ if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) {
++ unsigned bclkdiv;
++
++ ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out);
++
++ bclkdiv = freq_out / (aif1_lrck_div * params_rate(params));
++ for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) {
++ if (ac101_bclkdivs[i] >= bclkdiv) {
++ break;
++ }
++ }
++ ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<<AIF1_BCLK_DIV), i<<AIF1_BCLK_DIV);
++ } else {
++ /* set pll clock source to BCLK if slave mode */
++ ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params),
++ freq_out);
++ }
++
++ #if _MASTER_MULTI_CODEC == _MASTER_AC101
++ /* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++ #endif
++
++ AC101_DBG("rate: %d , channels: %d , samp_res: %d",
++ params_rate(params), channels, aif1_slot_size);
++
++ AC101_DBG("---\n");
++ return 0;
++}
++
++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
++{
++ int reg_val;
++ int AIF_CLK_CTRL = AIF1_CLK_CTRL;
++ struct snd_soc_codec *codec = codec_dai->codec;
++
++ AC101_DBG();
++
++ /*
++ * master or slave selection
++ * 0 = Master mode
++ * 1 = Slave mode
++ */
++ reg_val = ac101_read(codec, AIF_CLK_CTRL);
++ reg_val &= ~(0x1<<AIF1_MSTR_MOD);
++ switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master, ap is slave*/
++ #if _MASTER_MULTI_CODEC == _MASTER_AC101
++ pr_warn("AC101 as Master\n");
++ reg_val |= (0x0<<AIF1_MSTR_MOD);
++ break;
++ #else
++ pr_warn("AC108 as Master\n");
++ #endif
++ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave, ap is master*/
++ pr_warn("AC101 as Slave\n");
++ reg_val |= (0x1<<AIF1_MSTR_MOD);
++ break;
++ default:
++ pr_err("unknwon master/slave format\n");
++ return -EINVAL;
++ }
++
++ /*
++ * Enable TDM mode
++ */
++ reg_val |= (0x1 << AIF1_TDMM_ENA);
++ ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++ /* i2s mode selection */
++ reg_val = ac101_read(codec, AIF_CLK_CTRL);
++ reg_val&=~(3<<AIF1_DATA_FMT);
++ switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK){
++ case SND_SOC_DAIFMT_I2S: /* I2S1 mode */
++ reg_val |= (0x0<<AIF1_DATA_FMT);
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J: /* Right Justified mode */
++ reg_val |= (0x2<<AIF1_DATA_FMT);
++ break;
++ case SND_SOC_DAIFMT_LEFT_J: /* Left Justified mode */
++ reg_val |= (0x1<<AIF1_DATA_FMT);
++ break;
++ case SND_SOC_DAIFMT_DSP_A: /* L reg_val msb after FRM LRC */
++ reg_val |= (0x3<<AIF1_DATA_FMT);
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ /* TODO: data offset set to 0 */
++ reg_val |= (0x3<<AIF1_DATA_FMT);
++ break;
++ default:
++ pr_err("%s, line:%d\n", __func__, __LINE__);
++ return -EINVAL;
++ }
++ ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++ /* DAI signal inversions */
++ reg_val = ac101_read(codec, AIF_CLK_CTRL);
++ switch(fmt & SND_SOC_DAIFMT_INV_MASK){
++ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + nor frame */
++ reg_val &= ~(0x1<<AIF1_LRCK_INV);
++ reg_val &= ~(0x1<<AIF1_BCLK_INV);
++ break;
++ case SND_SOC_DAIFMT_NB_IF: /* normal bclk + inv frm */
++ reg_val |= (0x1<<AIF1_LRCK_INV);
++ reg_val &= ~(0x1<<AIF1_BCLK_INV);
++ break;
++ case SND_SOC_DAIFMT_IB_NF: /* invert bclk + nor frm */
++ reg_val &= ~(0x1<<AIF1_LRCK_INV);
++ reg_val |= (0x1<<AIF1_BCLK_INV);
++ break;
++ case SND_SOC_DAIFMT_IB_IF: /* invert bclk + inv frm */
++ reg_val |= (0x1<<AIF1_LRCK_INV);
++ reg_val |= (0x1<<AIF1_BCLK_INV);
++ break;
++ }
++ ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++ return 0;
++}
++
++int ac101_audio_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *codec_dai)
++{
++ // struct snd_soc_codec *codec = codec_dai->codec;
++
++ AC101_DBG("\n\n\n");
++
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++ }
++ return 0;
++}
++
++int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int ret = 0;
++
++ AC101_DBG("stream=%s cmd=%d\n",
++ snd_pcm_stream_str(substream),
++ cmd);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ #if _MASTER_MULTI_CODEC == _MASTER_AC101
++ if (ac10x->aif1_clken == 0){
++ /*
++ * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable'
++ * Or else the two AC108 chips lost the sync.
++ */
++ ret = 0;
++ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
++ (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
++ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
++ (0x1<<MOD_RESET_AIF1));
++ }
++ #endif
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++#if 0
++static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ AC101_DBG("id=%d freq=%d, dir=%d\n",
++ clk_id, freq, dir);
++
++ ac10x->sysclk = freq;
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops ac101_aif1_dai_ops = {
++ //.startup = ac101_audio_startup,
++ //.shutdown = ac101_aif_shutdown,
++ //.set_sysclk = ac101_set_dai_sysclk,
++ //.set_pll = ac101_set_pll,
++ //.set_fmt = ac101_set_dai_fmt,
++ //.hw_params = ac101_hw_params,
++ //.trigger = ac101_trigger,
++ //.digital_mute = ac101_aif_mute,
++};
++
++static struct snd_soc_dai_driver ac101_dai[] = {
++ {
++ .name = "ac10x-aif1",
++ .id = AIF1_CLK,
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = AC101_RATES,
++ .formats = AC101_FORMATS,
++ },
++ #if 0
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = AC101_RATES,
++ .formats = AC101_FORMATS,
++ },
++ #endif
++ .ops = &ac101_aif1_dai_ops,
++ }
++};
++#endif
++
++static void codec_resume_work(struct work_struct *work)
++{
++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume);
++ struct snd_soc_codec *codec = ac10x->codec;
++
++ AC101_DBG("+++\n");
++
++ set_configuration(codec);
++ if (drc_used) {
++ drc_config(codec);
++ }
++ /*enable this bit to prevent leakage from ldoin*/
++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
++
++ AC101_DBG("---\n");
++ return;
++}
++
++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
++{
++ switch (level) {
++ case SND_SOC_BIAS_ON:
++ AC101_DBG("SND_SOC_BIAS_ON\n");
++ break;
++ case SND_SOC_BIAS_PREPARE:
++ AC101_DBG("SND_SOC_BIAS_PREPARE\n");
++ break;
++ case SND_SOC_BIAS_STANDBY:
++ AC101_DBG("SND_SOC_BIAS_STANDBY\n");
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ switch_hw_config(codec);
++ #endif
++ break;
++ case SND_SOC_BIAS_OFF:
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0<<HBIASEN));
++ ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0<<HBIASADCEN));
++ #endif
++ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++ (0<<HPOUTPUTENABLE));
++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0<<OSCEN));
++ AC101_DBG("SND_SOC_BIAS_OFF\n");
++ break;
++ }
++ snd_soc_codec_get_dapm(codec)->bias_level = level;
++ return 0;
++}
++
++int ac101_codec_probe(struct snd_soc_codec *codec)
++{
++ int ret = 0;
++ struct ac10x_priv *ac10x;
++
++ ac10x = dev_get_drvdata(codec->dev);
++ if (ac10x == NULL) {
++ AC101_DBG("not set client data!\n");
++ return -ENOMEM;
++ }
++ ac10x->codec = codec;
++
++ INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play);
++ INIT_WORK(&ac10x->codec_resume, codec_resume_work);
++ ac10x->dac_enable = 0;
++ ac10x->aif1_clken = 0;
++ mutex_init(&ac10x->dac_mutex);
++
++ set_configuration(ac10x->codec);
++
++ /*enable this bit to prevent leakage from ldoin*/
++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
++ ac101_write(codec, DAC_VOL_CTRL, 0);
++
++ /* customized get/put inteface */
++ for (ret = 0; ret < ARRAY_SIZE(ac101_controls); ret++) {
++ struct snd_kcontrol_new* skn = &ac101_controls[ret];
++
++ skn->get = snd_ac101_get_volsw;
++ skn->put = snd_ac101_put_volsw;
++ }
++ ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls));
++ if (ret) {
++ pr_err("[ac10x] Failed to register audio mode control, "
++ "will continue without it.\n");
++ }
++
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ ret = ac101_switch_probe(ac10x);
++ if (ret) {
++ // not care the switch return value
++ }
++ #endif
++
++ return 0;
++}
++
++/* power down chip */
++int ac101_codec_remove(struct snd_soc_codec *codec)
++{
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ if (ac10x->irq) {
++ devm_free_irq(codec->dev, ac10x->irq, ac10x);
++ ac10x->irq = 0;
++ }
++
++ if (cancel_work_sync(&ac10x->work_switch) != 0) {
++ }
++
++ if (cancel_work_sync(&ac10x->work_clear_irq) != 0) {
++ }
++
++ if (ac10x->inpdev) {
++ input_unregister_device(ac10x->inpdev);
++ ac10x->inpdev = NULL;
++ }
++ #endif
++
++ return 0;
++}
++
++int ac101_codec_suspend(struct snd_soc_codec *codec)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ AC101_DBG("[codec]:suspend\n");
++ regcache_cache_only(ac10x->regmap101, true);
++ return 0;
++}
++
++int ac101_codec_resume(struct snd_soc_codec *codec)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int ret;
++
++ AC101_DBG("[codec]:resume");
++
++ /* Sync reg_cache with the hardware */
++ regcache_cache_only(ac10x->regmap101, false);
++ ret = regcache_sync(ac10x->regmap101);
++ if (ret != 0) {
++ dev_err(codec->dev, "Failed to sync register cache: %d\n", ret);
++ regcache_cache_only(ac10x->regmap101, true);
++ return ret;
++ }
++
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ ac10x->mode = HEADPHONE_IDLE;
++ ac10x->state = -1;
++ #endif
++
++ ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++ schedule_work(&ac10x->codec_resume);
++ return 0;
++}
++
++/***************************************************************************/
++static ssize_t ac101_debug_store(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct ac10x_priv *ac10x = dev_get_drvdata(dev);
++ int val = 0, flag = 0;
++ u16 value_w, value_r;
++ u8 reg, num, i=0;
++
++ val = simple_strtol(buf, NULL, 16);
++ flag = (val >> 24) & 0xF;
++ if (flag) {
++ reg = (val >> 16) & 0xFF;
++ value_w = val & 0xFFFF;
++ ac101_write(ac10x->codec, reg, value_w);
++ printk("write 0x%x to reg:0x%x\n", value_w, reg);
++ } else {
++ reg = (val >> 8) & 0xFF;
++ num = val & 0xff;
++ printk("\n");
++ printk("read:start add:0x%x,count:0x%x\n", reg, num);
++
++ regcache_cache_bypass(ac10x->regmap101, true);
++ do {
++ value_r = ac101_read(ac10x->codec, reg);
++ printk("0x%x: 0x%04x ", reg++, value_r);
++ if (++i % 4 == 0 || i == num)
++ printk("\n");
++ } while (i < num);
++ regcache_cache_bypass(ac10x->regmap101, false);
++ }
++ return count;
++}
++static ssize_t ac101_debug_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ printk("echo flag|reg|val > ac10x\n");
++ printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n");
++ printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n");
++ return 0;
++}
++static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store);
++
++static struct attribute *audio_debug_attrs[] = {
++ &dev_attr_ac10x.attr,
++ NULL,
++};
++
++static struct attribute_group audio_debug_attr_group = {
++ .name = "ac101_debug",
++ .attrs = audio_debug_attrs,
++};
++/***************************************************************************/
++
++/************************************************************/
++static bool ac101_volatile_reg(struct device *dev, unsigned int reg)
++{
++ switch (reg) {
++ case PLL_CTRL2:
++ case HMIC_STS:
++ return true;
++ }
++ return false;
++}
++
++static const struct regmap_config ac101_regmap = {
++ .reg_bits = 8,
++ .val_bits = 16,
++ .reg_stride = 1,
++ .max_register = 0xB5,
++ .cache_type = REGCACHE_FLAT,
++ .volatile_reg = ac101_volatile_reg,
++};
++
++/* Sync reg_cache from the hardware */
++int ac10x_fill_regcache(struct device* dev, struct regmap* map) {
++ int r, i, n;
++ int v;
++
++ n = regmap_get_max_register(map);
++ for (i = 0; i < n; i++) {
++ regcache_cache_bypass(map, true);
++ r = regmap_read(map, i, &v);
++ if (r) {
++ dev_dbg(dev, "failed to read register %d\n", i);
++ continue;
++ }
++ regcache_cache_bypass(map, false);
++
++ regcache_cache_only(map, true);
++ r = regmap_write(map, i, v);
++ regcache_cache_only(map, false);
++ }
++ regcache_cache_bypass(map, false);
++ regcache_cache_only(map, false);
++
++ return 0;
++}
++
++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
++{
++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
++ int ret = 0;
++ unsigned v = 0;
++
++ AC101_DBG();
++
++ static_ac10x = ac10x;
++
++ ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap);
++ if (IS_ERR(ac10x->regmap101)) {
++ ret = PTR_ERR(ac10x->regmap101);
++ dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret);
++ return ret;
++ }
++
++ /* Chip reset */
++ regcache_cache_only(ac10x->regmap101, false);
++ ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
++ msleep(50);
++
++ /* sync regcache for FLAT type */
++ ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
++
++ ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v);
++ if (ret < 0) {
++ dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret);
++ return ret;
++ }
++
++ if (v != AC101_CHIP_ID) {
++ dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v);
++ dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID);
++ return -ENODEV;
++ }
++
++ ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group);
++ if (ret) {
++ pr_err("failed to create attr group\n");
++ }
++
++ ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(ac10x->gpiod_spk_amp_gate)) {
++ ac10x->gpiod_spk_amp_gate = NULL;
++ dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n");
++ }
++
++ return 0;
++}
++
++void ac101_shutdown(struct i2c_client *i2c)
++{
++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
++ struct snd_soc_codec *codec = ac10x->codec;
++ int reg_val;
++
++ if (codec == NULL) {
++ pr_err(": no sound card.\n");
++ return;
++ }
++
++ /*set headphone volume to 0*/
++ reg_val = ac101_read(codec, HPOUT_CTRL);
++ reg_val &= ~(0x3f<<HP_VOL);
++ ac101_write(codec, HPOUT_CTRL, reg_val);
++
++ /*disable pa*/
++ reg_val = ac101_read(codec, HPOUT_CTRL);
++ reg_val &= ~(0x1<<HPPA_EN);
++ ac101_write(codec, HPOUT_CTRL, reg_val);
++
++ /*hardware xzh support*/
++ reg_val = ac101_read(codec, OMIXER_DACA_CTRL);
++ reg_val &= ~(0xf<<HPOUTPUTENABLE);
++ ac101_write(codec, OMIXER_DACA_CTRL, reg_val);
++
++ /*unmute l/r headphone pa*/
++ reg_val = ac101_read(codec, HPOUT_CTRL);
++ reg_val &= ~((0x1<<RHPPA_MUTE)|(0x1<<LHPPA_MUTE));
++ ac101_write(codec, HPOUT_CTRL, reg_val);
++ return;
++}
++
++int ac101_remove(struct i2c_client *i2c)
++{
++ sysfs_remove_group(&i2c->dev.kobj, &audio_debug_attr_group);
++ return 0;
++}
++
++MODULE_DESCRIPTION("ASoC ac10x driver");
++MODULE_AUTHOR("huangxin,liushaohua");
++MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");
+--- /dev/null
++++ b/sound/soc/codecs/ac101_regs.h
+@@ -0,0 +1,431 @@
++/*
++ * ac101_regs.h
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2010-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ * huangxin <huangxin@reuuimllatech.com>
++ *
++ * some simple description for this code
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++#ifndef __AC101_REGS_H__
++#define __AC101_REGS_H__
++
++/*pll source*/
++#define AC101_MCLK1 1
++#define AC101_MCLK2 2
++#define AC101_BCLK1 3
++#define AC101_BCLK2 4
++
++#define AIF1_CLK 1
++#define AIF2_CLK 2
++
++#define CHIP_AUDIO_RST 0x0
++#define PLL_CTRL1 0x1
++#define PLL_CTRL2 0x2
++#define SYSCLK_CTRL 0x3
++#define MOD_CLK_ENA 0x4
++#define MOD_RST_CTRL 0x5
++#define AIF_SR_CTRL 0x6
++
++#define AIF1_CLK_CTRL 0x10
++#define AIF1_ADCDAT_CTRL 0x11
++#define AIF1_DACDAT_CTRL 0x12
++#define AIF1_MXR_SRC 0x13
++#define AIF1_VOL_CTRL1 0x14
++#define AIF1_VOL_CTRL2 0x15
++#define AIF1_VOL_CTRL3 0x16
++#define AIF1_VOL_CTRL4 0x17
++#define AIF1_MXR_GAIN 0x18
++#define AIF1_RXD_CTRL 0x19
++#define ADC_DIG_CTRL 0x40
++#define ADC_VOL_CTRL 0x41
++#define ADC_DBG_CTRL 0x42
++
++#define HMIC_CTRL1 0x44
++#define HMIC_CTRL2 0x45
++#define HMIC_STS 0x46
++
++#define DAC_DIG_CTRL 0x48
++#define DAC_VOL_CTRL 0x49
++#define DAC_DBG_CTRL 0x4a
++#define DAC_MXR_SRC 0x4c
++#define DAC_MXR_GAIN 0x4d
++
++#define ADC_APC_CTRL 0x50
++#define ADC_SRC 0x51
++#define ADC_SRCBST_CTRL 0x52
++#define OMIXER_DACA_CTRL 0x53
++#define OMIXER_SR 0x54
++#define OMIXER_BST1_CTRL 0x55
++#define HPOUT_CTRL 0x56
++#define ESPKOUT_CTRL 0x57
++#define SPKOUT_CTRL 0x58
++#define LOUT_CTRL 0x59
++#define ADDA_TUNE1 0x5a
++#define ADDA_TUNE2 0x5b
++#define ADDA_TUNE3 0x5c
++#define HPOUT_STR 0x5d
++
++/*CHIP_AUDIO_RST*/
++#define AC101_CHIP_ID 0x0101
++
++/*PLL_CTRL1*/
++#define DPLL_DAC_BIAS 14
++#define PLL_POSTDIV_M 8
++#define CLOSE_LOOP 6
++#define INT 0
++
++/*PLL_CTRL2*/
++#define PLL_EN 15
++#define PLL_LOCK_STATUS 14
++#define PLL_PREDIV_NI 4
++#define PLL_POSTDIV_NF 0
++
++/*SYSCLK_CTRL*/
++#define PLLCLK_ENA 15
++#define PLLCLK_SRC 12
++#define AIF1CLK_ENA 11
++#define AIF1CLK_SRC 8
++#define AIF2CLK_ENA 7
++#define AIF2CLK_SRC 4
++#define SYSCLK_ENA 3
++#define SYSCLK_SRC 0
++
++/*MOD_CLK_ENA*/
++#define MOD_CLK_AIF1 15
++#define MOD_CLK_AIF2 14
++#define MOD_CLK_AIF3 13
++#define MOD_CLK_SRC1 11
++#define MOD_CLK_SRC2 10
++#define MOD_CLK_HPF_AGC 7
++#define MOD_CLK_HPF_DRC 6
++#define MOD_CLK_ADC_DIG 3
++#define MOD_CLK_DAC_DIG 2
++
++/*MOD_RST_CTRL*/
++#define MOD_RESET_CTL 0
++#define MOD_RESET_AIF1 15
++#define MOD_RESET_AIF2 14
++#define MOD_RESET_AIF3 13
++#define MOD_RESET_SRC1 11
++#define MOD_RESET_SRC2 10
++#define MOD_RESET_HPF_AGC 7
++#define MOD_RESET_HPF_DRC 6
++#define MOD_RESET_ADC_DIG 3
++#define MOD_RESET_DAC_DIG 2
++
++/*AIF_SR_CTRL*/
++#define AIF1_FS 12 //AIF1 Sample Rate
++#define AIF2_FS 8 //AIF2 Sample Rate
++#define SRC1_ENA 3
++#define SRC1_SRC 2
++#define SRC2_ENA 1
++#define SRC2_SRC 0
++
++/*AIF1LCK_CTRL*/
++#define AIF1_MSTR_MOD 15
++#define AIF1_BCLK_INV 14
++#define AIF1_LRCK_INV 13
++#define AIF1_BCLK_DIV 9
++#define AIF1_LRCK_DIV 6
++#define AIF1_WORK_SIZ 4
++#define AIF1_DATA_FMT 2
++#define DSP_MONO_PCM 1
++#define AIF1_TDMM_ENA 0
++
++/*AIF1_ADCDAT_CTRL*/
++#define AIF1_AD0L_ENA 15
++#define AIF1_AD0R_ENA 14
++#define AIF1_AD1L_ENA 13
++#define AIF1_AD1R_ENA 12
++#define AIF1_AD0L_SRC 10
++#define AIF1_AD0R_SRC 8
++#define AIF1_AD1L_SRC 6
++#define AIF1_AD1R_SRC 4
++#define AIF1_ADCP_ENA 3
++#define AIF1_ADUL_ENA 2
++#define AIF1_SLOT_SIZ 0
++
++/*AIF1_DACDAT_CTRL*/
++#define AIF1_DA0L_ENA 15
++#define AIF1_DA0R_ENA 14
++#define AIF1_DA1L_ENA 13
++#define AIF1_DA1R_ENA 12
++#define AIF1_DA0L_SRC 10
++#define AIF1_DA0R_SRC 8
++#define AIF1_DA1L_SRC 6
++#define AIF1_DA1R_SRC 4
++#define AIF1_DACP_ENA 3
++#define AIF1_DAUL_ENA 2
++#define AIF1_SLOT_SIZ 0
++
++/*AIF1_MXR_SRC*/
++#define AIF1_AD0L_AIF1_DA0L_MXR 15
++#define AIF1_AD0L_AIF2_DACL_MXR 14
++#define AIF1_AD0L_ADCL_MXR 13
++#define AIF1_AD0L_AIF2_DACR_MXR 12
++#define AIF1_AD0R_AIF1_DA0R_MXR 11
++#define AIF1_AD0R_AIF2_DACR_MXR 10
++#define AIF1_AD0R_ADCR_MXR 9
++#define AIF1_AD0R_AIF2_DACL_MXR 8
++#define AIF1_AD1L_AIF2_DACL_MXR 7
++#define AIF1_AD1L_ADCL_MXR 6
++#define AIF1_AD1L_MXR_SRC 6
++#define AIF1_AD1R_AIF2_DACR_MXR 3
++#define AIF1_AD1R_ADCR_MXR 2
++#define AIF1_AD1R_MXR_SRC 2
++
++/*AIF1_VOL_CTRL1*/
++#define AIF1_AD0L_VOL 8
++#define AIF1_AD0R_VOL 0
++
++/*AIF1_VOL_CTRL2*/
++#define AIF1_AD1L_VOL 8
++#define AIF1_AD1R_VOL 0
++
++/*AIF1_VOL_CTRL3*/
++#define AIF1_DA0L_VOL 8
++#define AIF1_DA0R_VOL 0
++
++/*AIF1_VOL_CTRL4*/
++#define AIF1_DA1L_VOL 8
++#define AIF1_DA1R_VOL 0
++
++/*AIF1_MXR_GAIN*/
++#define AIF1_AD0L_MXR_GAIN 12
++#define AIF1_AD0R_MXR_GAIN 8
++#define AIF1_AD1L_MXR_GAIN 6
++#define AIF1_AD1R_MXR_GAIN 2
++
++/*AIF1_RXD_CTRL*/
++#define AIF1_N_DATA_DISCARD 8
++
++/*ADC_DIG_CTRL*/
++#define ENAD 15
++#define ENDM 14
++#define ADFIR32 13
++#define ADOUT_DTS 2
++#define ADOUT_DLY 1
++
++/*ADC_VOL_CTRL*/
++#define ADC_VOL_L 8
++#define ADC_VOL_R 0
++
++/*ADC_DBG_CTRL*/
++#define ADSW 15
++#define DMIC_CLK_PIN_CTRL 12
++
++/*HMIC_CTRL1*/
++#define HMIC_M 12
++#define HMIC_N 8
++#define HMIC_DATA_IRQ_MODE 7
++#define HMIC_TH1_HYSTERESIS 5
++#define HMIC_PULLOUT_IRQ 4
++#define HMIC_PLUGIN_IRQ 3
++#define HMIC_KEYUP_IRQ 2
++#define HMIC_KEYDOWN_IRQ 1
++#define HMIC_DATA_IRQ_EN 0
++
++/*HMIC_CTRL2*/
++#define HMIC_SAMPLE_SELECT 14
++#define HMIC_TH2_HYSTERESIS 13
++#define HMIC_TH2 8
++#define HMIC_SF 6
++#define KEYUP_CLEAR 5
++#define HMIC_TH1 0
++
++/*HMIC_STS*/
++#define HMIC_DATA 8
++#define GET_HMIC_DATA(r) (((r) >> HMIC_DATA) & 0x1F)
++#define HMIC_PULLOUT_PEND 4
++#define HMIC_PLUGIN_PEND 3
++#define HMIC_KEYUP_PEND 2
++#define HMKC_KEYDOWN_PEND 1
++#define HMIC_DATA_PEND 0
++#define HMIC_PEND_ALL (0x1F)
++
++/*DAC_DIG_CTRL*/
++#define ENDA 15
++#define ENHPF 14
++#define DAFIR32 13
++#define MODQU 8
++
++/*DAC_VOL_CTRL*/
++#define DAC_VOL_L 8
++#define DAC_VOL_R 0
++
++/*DAC_DBG_CTRL*/
++#define DASW 15
++#define ENDWA_N 14
++#define DAC_MOD_DBG 13
++#define DAC_PTN_SEL 6
++#define DVC 0
++
++/*DAC_MXR_SRC*/
++#define DACL_MXR_AIF1_DA0L 15
++#define DACL_MXR_AIF1_DA1L 14
++#define DACL_MXR_AIF2_DACL 13
++#define DACL_MXR_ADCL 12
++#define DACL_MXR_SRC 12
++#define DACR_MXR_AIF1_DA0R 11
++#define DACR_MXR_AIF1_DA1R 10
++#define DACR_MXR_AIF2_DACR 9
++#define DACR_MXR_ADCR 8
++#define DACR_MXR_SRC 8
++
++/*DAC_MXR_GAIN*/
++#define DACL_MXR_GAIN 12
++#define DACR_MXR_GAIN 8
++
++/*ADC_APC_CTRL*/
++#define ADCREN 15
++#define ADCRG 12
++#define ADCLEN 11
++#define ADCLG 8
++#define MBIASEN 7
++#define MMIC_BIAS_CHOP_EN 6
++#define MMIC_BIAS_CHOP_CKS 4
++#define HBIASMOD 2
++#define HBIASEN 1
++#define HBIASADCEN 0
++
++/*ADC_SRC*/
++#define RADCMIXMUTEMIC1BOOST (13)
++#define RADCMIXMUTEMIC2BOOST (12)
++#define RADCMIXMUTELINEINLR (11)
++#define RADCMIXMUTELINEINR (10)
++#define RADCMIXMUTEAUXINR (9)
++#define RADCMIXMUTEROUTPUT (8)
++#define RADCMIXMUTELOUTPUT (7)
++#define LADCMIXMUTEMIC1BOOST (6)
++#define LADCMIXMUTEMIC2BOOST (5)
++#define LADCMIXMUTELINEINLR (4)
++#define LADCMIXMUTELINEINL (3)
++#define LADCMIXMUTEAUXINL (2)
++#define LADCMIXMUTELOUTPUT (1)
++#define LADCMIXMUTEROUTPUT (0)
++
++/*ADC_SRCBST_CTRL*/
++#define MIC1AMPEN 15
++#define ADC_MIC1G 12
++#define MIC2AMPEN 11
++#define ADC_MIC2G 8
++#define MIC2SLT 7
++#define LINEIN_PREG 4
++#define AUXI_PREG 0
++
++/*OMIXER_DACA_CTRL*/
++#define DACAREN 15
++#define DACALEN 14
++#define RMIXEN 13
++#define LMIXEN 12
++#define HPOUTPUTENABLE 8
++
++/*OMIXER_SR*/
++#define RMIXMUTEMIC1BOOST (13)
++#define RMIXMUTEMIC2BOOST (12)
++#define RMIXMUTELINEINLR (11)
++#define RMIXMUTELINEINR (10)
++#define RMIXMUTEAUXINR (9)
++#define RMIXMUTEDACR (8)
++#define RMIXMUTEDACL (7)
++#define LMIXMUTEMIC1BOOST (6)
++#define LMIXMUTEMIC2BOOST (5)
++#define LMIXMUTELINEINLR (4)
++#define LMIXMUTELINEINL (3)
++#define LMIXMUTEAUXINL (2)
++#define LMIXMUTEDACL (1)
++#define LMIXMUTEDACR (0)
++
++/*OMIXER_BST1_CTRL*/
++#define BIASVOLTAGE 12
++#define AXG 9
++#define OMIXER_MIC1G 6
++#define OMIXER_MIC2G 3
++#define LINEING 0
++
++/*HPOUT_CTRL*/
++#define RHPS 15
++#define LHPS 14
++#define RHPPA_MUTE 13
++#define LHPPA_MUTE 12
++#define HPPA_EN 11
++#define HP_VOL 4
++#define HPPA_DEL 2
++#define HPPA_IS 0
++
++/*ESPKOUT_CTRL*/
++#define EAR_RAMP_TIME 11
++#define ESPA_OUT_CURRENT 9
++#define ESPSR 7
++#define ESPPA_MUTE 6
++#define ESPPA_EN 5
++#define ESP_VOL 0
++
++/*SPKOUT_CTRL*/
++#define HPCALICKS 13
++#define RSPKS 12
++#define RSPKINVEN 11
++#define RSPK_EN 9
++#define LSPKS 8
++#define LSPKINVEN 7
++#define LSPK_EN 5
++#define SPK_VOL 0
++
++/*LOUT_CTRL*/
++#define LINEOUTG 5
++#define LINEOUTEN 4
++#define LINEOUTS0 3
++#define LINEOUTS1 2
++#define LINEOUTS2 1
++#define LINEOUTS3 0
++
++/*ADDA_TUNE1*/
++#define CURRENT_TEST_SELECT 14
++#define BIHE_CTRL 12
++#define DITHER 11
++#define DITHER_CLK 9
++#define ZERO_CROSSOVER_EN 8
++#define ZERO_CROSSOVER_TIME 7
++#define EAR_SPEED_SELECT 6
++#define REF_CHOPPEN_CKS 4
++#define OPMIC_BIAS_CUR 0
++
++/*ADDA_TUNE2*/
++#define OPDAC_BIAS_CUR 14
++#define OPDRV_BIAS_CUR 12
++#define OPMIX_BIAS_CUR 10
++#define OPEAR_BIAS_CUR 8
++#define OPVR_BIAS_CUR 6
++#define OPAAF_BIAS_CUR 4
++#define OPADC1_BIAS_CUR 2
++#define OPADC2_BIAS_CUR 0
++
++/*ADDA_TUNE3*/
++#define LDOEN 15
++#define LDO_SEL 12
++#define BIASCALIVERIFY 11
++#define BIASMODE 10
++#define BIASCALIDATA 9
++#define OSCS 1
++#define OSCEN 0
++
++/*HPOUT_STR*/
++#define HPVL_SOFT_MOD 14
++#define HPVL_STEP_CTRL 8
++#define DACA_CHND_ENA 7
++#define HPPA_MXRD_ENA 6
++#define HPVL_CTRL_OUT 0
++
++#endif//__AC101_REGS_H__
+--- /dev/null
++++ b/sound/soc/codecs/ac108.c
+@@ -0,0 +1,1622 @@
++/*
++ * ac10x.c -- ac10x ALSA SoC Audio driver
++ *
++ *
++ * Author: Baozhu Zuo<zuobaozhu@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/pm.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/tlv.h>
++
++#include "ac108.h"
++#include "ac10x.h"
++
++#define _USE_CAPTURE 1
++#define _MASTER_INDEX 0
++
++/* #undef DEBUG
++ * use 'make DEBUG=1' to enable debugging
++ */
++
++/**
++ * TODO:
++ * 1, add PM API: ac108_suspend,ac108_resume
++ * 2,0x65-0x6a
++ * 3,0x76-0x79 high 4bit
++ */
++struct pll_div {
++ unsigned int freq_in;
++ unsigned int freq_out;
++ unsigned int m1;
++ unsigned int m2;
++ unsigned int n;
++ unsigned int k1;
++ unsigned int k2;
++};
++
++static struct ac10x_priv *ac10x;
++
++struct real_val_to_reg_val {
++ unsigned int real_val;
++ unsigned int reg_val;
++};
++
++static const struct real_val_to_reg_val ac108_sample_rate[] = {
++ { 8000, 0 },
++ { 11025, 1 },
++ { 12000, 2 },
++ { 16000, 3 },
++ { 22050, 4 },
++ { 24000, 5 },
++ { 32000, 6 },
++ { 44100, 7 },
++ { 48000, 8 },
++ { 96000, 9 },
++};
++
++/* Sample resolution */
++static const struct real_val_to_reg_val ac108_samp_res[] = {
++ { 8, 1 },
++ { 12, 2 },
++ { 16, 3 },
++ { 20, 4 },
++ { 24, 5 },
++ { 28, 6 },
++ { 32, 7 },
++};
++
++static const unsigned int ac108_bclkdivs[] = {
++ 0, 1, 2, 4,
++ 6, 8, 12, 16,
++ 24, 32, 48, 64,
++ 96, 128, 176, 192,
++};
++
++/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */
++static const struct pll_div ac108_pll_div_list[] = {
++ { 400000, _FREQ_24_576K, 0, 0, 614, 4, 1 },
++ { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, /* _FREQ_24_576K/48 */
++ { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, /* _FREQ_24_576K/32 */
++ { 800000, _FREQ_24_576K, 0, 0, 614, 9, 1 },
++ { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, /* _FREQ_24_576K/24 */
++ { 1600000, _FREQ_24_576K, 0, 0, 307, 9, 1 },
++ { 2048000, _FREQ_24_576K, 0, 0, 240, 9, 1 }, /* accurate, 8000 * 256 */
++ { 3072000, _FREQ_24_576K, 0, 0, 160, 9, 1 }, /* accurate, 12000 * 256 */
++ { 4096000, _FREQ_24_576K, 2, 0, 360, 9, 1 }, /* accurate, 16000 * 256 */
++ { 6000000, _FREQ_24_576K, 4, 0, 410, 9, 1 },
++ { 12000000, _FREQ_24_576K, 9, 0, 410, 9, 1 },
++ { 13000000, _FREQ_24_576K, 8, 0, 340, 9, 1 },
++ { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 },
++ { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 },
++ { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 },
++ { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 },
++ { 24000000, _FREQ_24_576K, 9, 0, 256,24, 0 }, /* accurate, 24M -> 24.576M */
++
++ { 400000, _FREQ_22_579K, 0, 0, 566, 4, 1 },
++ { 512000, _FREQ_22_579K, 0, 0, 880, 9, 1 },
++ { 768000, _FREQ_22_579K, 0, 0, 587, 9, 1 },
++ { 800000, _FREQ_22_579K, 0, 0, 567, 9, 1 },
++ { 1024000, _FREQ_22_579K, 0, 0, 440, 9, 1 },
++ { 1600000, _FREQ_22_579K, 1, 0, 567, 9, 1 },
++ { 2048000, _FREQ_22_579K, 0, 0, 220, 9, 1 },
++ { 3072000, _FREQ_22_579K, 0, 0, 148, 9, 1 },
++ { 4096000, _FREQ_22_579K, 2, 0, 330, 9, 1 },
++ { 6000000, _FREQ_22_579K, 2, 0, 227, 9, 1 },
++ { 12000000, _FREQ_22_579K, 8, 0, 340, 9, 1 },
++ { 13000000, _FREQ_22_579K, 9, 0, 350, 9, 1 },
++ { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 },
++ { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 },
++ { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 },
++ { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 },
++ { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */
++
++
++ { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, /* _FREQ_24_576K */
++ { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */
++ { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */
++ { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, /* 1536000 */
++ { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, /* 384000 */
++ { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, /* 256000 */
++ { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, /* 192000 */
++ { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, /* 140000 */
++ { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, /* 128000 */
++
++ { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, /* _FREQ_22_579K */
++ { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */
++ { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */
++ { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, /* 1411200 */
++ { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, /* 352800 */
++ { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, /* 235200 */
++ { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, /* 176400 */
++ { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, /* 128290 */
++ { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, /* 117600 */
++
++ { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, /* 3763200 */
++ { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */
++ { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, /* 1881600 */
++ { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, /* 940800 */
++ { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, /* 705600 */
++ { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, /* 470400 */
++};
++
++
++/* AC108 definition */
++#define AC108_CHANNELS_MAX 8 /* range[1, 16] */
++#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 & \
++ ~(SNDRV_PCM_RATE_64000 | \
++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000))
++#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
++ /*SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE |*/ \
++ SNDRV_PCM_FMTBIT_S32_LE)
++
++static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
++static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0);
++
++int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm)
++{
++ int r, v = 0;
++
++ if ((r = regmap_read(i2cm, reg, &v)) < 0)
++ pr_info("ac10x_read info->[REG-0x%02x]\n", reg);
++ else
++ *rt_val = v;
++ return r;
++}
++
++int ac10x_write(u8 reg, u8 val, struct regmap* i2cm)
++{
++ int r;
++
++ if ((r = regmap_write(i2cm, reg, val)) < 0)
++ pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val);
++ return r;
++}
++
++int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm)
++{
++ int r;
++
++ if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0)
++ pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
++ return r;
++}
++
++/**
++ * snd_ac108_get_volsw - single mixer get callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to get the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct soc_mixer_control *mc =
++ (struct soc_mixer_control *)kcontrol->private_value;
++ unsigned int mask = (1 << fls(mc->max)) - 1;
++ unsigned int invert = mc->invert;
++ int ret, chip = mc->autodisable;
++ u8 val;
++
++ if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0)
++ return ret;
++
++ val = ((val >> mc->shift) & mask) - mc->min;
++ if (invert) {
++ val = mc->max - val;
++ }
++ ucontrol->value.integer.value[0] = val;
++ return 0;
++}
++
++/**
++ * snd_ac108_put_volsw - single mixer put callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to set the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct soc_mixer_control *mc =
++ (struct soc_mixer_control *)kcontrol->private_value;
++ unsigned int sign_bit = mc->sign_bit;
++ unsigned int val, mask = (1 << fls(mc->max)) - 1;
++ unsigned int invert = mc->invert;
++ int ret, chip = mc->autodisable;
++
++ if (sign_bit)
++ mask = BIT(sign_bit + 1) - 1;
++
++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
++ if (invert) {
++ val = mc->max - val;
++ }
++
++ mask = mask << mc->shift;
++ val = val << mc->shift;
++
++ ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]);
++ return ret;
++}
++
++#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
++ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
++ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++ .tlv.p = (tlv_array), \
++ .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\
++ .put = snd_ac108_put_volsw, \
++ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) }
++
++/* single ac108 */
++static const struct snd_kcontrol_new ac108_snd_controls[] = {
++ /* ### chip 0 ### */
++ /*0x70: ADC1 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x71: ADC2 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x72: ADC3 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x73: ADC4 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++
++ /*0x90: Analog PGA1 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x91: Analog PGA2 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x92: Analog PGA3 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x93: Analog PGA4 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++};
++/* multiple ac108s */
++static const struct snd_kcontrol_new ac108tdm_snd_controls[] = {
++ /* ### chip 1 ### */
++ /*0x70: ADC1 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
++ 0, 0xff, 0, 1, tlv_ch_digital_vol),
++ /*0x71: ADC2 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
++ 0, 0xff, 0, 1, tlv_ch_digital_vol),
++ /*0x72: ADC3 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
++ 0, 0xff, 0, 1, tlv_ch_digital_vol),
++ /*0x73: ADC4 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
++ 0, 0xff, 0, 1, tlv_ch_digital_vol),
++
++ /*0x90: Analog PGA1 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
++ ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++ /*0x91: Analog PGA2 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
++ ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++ /*0x92: Analog PGA3 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
++ ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++ /*0x93: Analog PGA4 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
++ ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++
++ /* ### chip 0 ### */
++ /*0x70: ADC1 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x71: ADC2 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x72: ADC3 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++ /*0x73: ADC4 Digital Channel Volume Control Register*/
++ SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL,
++ 0, 0xff, 0, 0, tlv_ch_digital_vol),
++
++ /*0x90: Analog PGA1 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL,
++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x91: Analog PGA2 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL,
++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x92: Analog PGA3 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL,
++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++ /*0x93: Analog PGA4 Control Register*/
++ SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL,
++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++};
++
++
++static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = {
++ /* input widgets */
++ SND_SOC_DAPM_INPUT("MIC1P"),
++ SND_SOC_DAPM_INPUT("MIC1N"),
++
++ SND_SOC_DAPM_INPUT("MIC2P"),
++ SND_SOC_DAPM_INPUT("MIC2N"),
++
++ SND_SOC_DAPM_INPUT("MIC3P"),
++ SND_SOC_DAPM_INPUT("MIC3N"),
++
++ SND_SOC_DAPM_INPUT("MIC4P"),
++ SND_SOC_DAPM_INPUT("MIC4N"),
++
++ SND_SOC_DAPM_INPUT("DMIC1"),
++ SND_SOC_DAPM_INPUT("DMIC2"),
++
++ /*0xa0: ADC1 Analog Control 1 Register*/
++ /*0xa1-0xa6:use the defualt value*/
++ SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1),
++ SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0),
++ SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1),
++
++ /*0xa7: ADC2 Analog Control 1 Register*/
++ /*0xa8-0xad:use the defualt value*/
++ SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1),
++ SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0),
++ SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1),
++
++ /*0xae: ADC3 Analog Control 1 Register*/
++ /*0xaf-0xb4:use the defualt value*/
++ SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1),
++ SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0),
++ SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1),
++
++ /*0xb5: ADC4 Analog Control 1 Register*/
++ /*0xb6-0xbb:use the defualt value*/
++ SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1),
++ SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0),
++ SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1),
++
++
++ /*0x61: ADC Digital Part Enable Register*/
++ SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4, 1, NULL, 0),
++ SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0, 1),
++ SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1, 1),
++ SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2, 1),
++ SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3, 1),
++
++ SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0),
++ SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0),
++ SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0),
++ SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0),
++
++ SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0),
++
++ /*0x62:Digital MIC Enable Register*/
++ SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0),
++ SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0),
++};
++
++static const struct snd_soc_dapm_route ac108_dapm_routes[] = {
++
++ { "ADC1", NULL, "Channel 1 AAF" },
++ { "ADC2", NULL, "Channel 2 AAF" },
++ { "ADC3", NULL, "Channel 3 AAF" },
++ { "ADC4", NULL, "Channel 4 AAF" },
++
++ { "Channel 1 AAF", NULL, "MIC1BIAS" },
++ { "Channel 2 AAF", NULL, "MIC2BIAS" },
++ { "Channel 3 AAF", NULL, "MIC3BIAS" },
++ { "Channel 4 AAF", NULL, "MIC4BIAS" },
++
++ { "MIC1BIAS", NULL, "ADC1 CLK" },
++ { "MIC2BIAS", NULL, "ADC2 CLK" },
++ { "MIC3BIAS", NULL, "ADC3 CLK" },
++ { "MIC4BIAS", NULL, "ADC4 CLK" },
++
++
++ { "ADC1 CLK", NULL, "DSM EN" },
++ { "ADC2 CLK", NULL, "DSM EN" },
++ { "ADC3 CLK", NULL, "DSM EN" },
++ { "ADC4 CLK", NULL, "DSM EN" },
++
++
++ { "DSM EN", NULL, "ADC EN" },
++
++ { "Channel 1 EN", NULL, "DSM EN" },
++ { "Channel 2 EN", NULL, "DSM EN" },
++ { "Channel 3 EN", NULL, "DSM EN" },
++ { "Channel 4 EN", NULL, "DSM EN" },
++
++
++ { "MIC1P", NULL, "Channel 1 EN" },
++ { "MIC1N", NULL, "Channel 1 EN" },
++
++ { "MIC2P", NULL, "Channel 2 EN" },
++ { "MIC2N", NULL, "Channel 2 EN" },
++
++ { "MIC3P", NULL, "Channel 3 EN" },
++ { "MIC3N", NULL, "Channel 3 EN" },
++
++ { "MIC4P", NULL, "Channel 4 EN" },
++ { "MIC4N", NULL, "Channel 4 EN" },
++
++};
++
++static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x)
++{
++ u8 i;
++ for (i = 0; i < ac10x->codec_cnt; i++)
++ ac10x_write(reg, val, ac10x->i2cmap[i]);
++ return 0;
++}
++
++static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x)
++{
++ int r = 0;
++ u8 i;
++ for (i = 0; i < ac10x->codec_cnt; i++)
++ r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
++ return r;
++}
++
++static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg)
++{
++ unsigned char val_r;
++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
++ /*read one chip is fine*/
++ ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]);
++ return val_r;
++}
++
++static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val)
++{
++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
++ ac108_multi_write(reg, val, ac10x);
++ return 0;
++}
++
++/**
++ * The Power management related registers are Reg01h~Reg09h
++ * 0x01-0x05,0x08,use the default value
++ * @author baozhu (17-6-21)
++ *
++ * @param ac10x
++ */
++static void ac108_configure_power(struct ac10x_priv *ac10x)
++{
++ /**
++ * 0x06:Enable Analog LDO
++ */
++ ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x);
++ /**
++ * 0x07:
++ * Control VREF output and micbias voltage ?
++ * REF faststart disable, enable Enable VREF (needed for Analog
++ * LDO and MICBIAS)
++ */
++ ac108_multi_update_bits(PWR_CTRL7,
++ 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE |
++ 0x01 << VREF_ENABLE,
++ 0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE |
++ 0x01 << VREF_ENABLE,
++ ac10x);
++ /**
++ * 0x09:
++ * Disable fast-start circuit on VREFP
++ * VREFP_RESCTRL=00=1 MOhm
++ * IGEN_TRIM=100=+25%
++ * Enable VREFP (needed by all audio input channels)
++ */
++ ac108_multi_update_bits(PWR_CTRL9,
++ 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL |
++ 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
++ 0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL |
++ 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
++ ac10x);
++}
++
++/**
++ * The clock management related registers are Reg20h~Reg25h
++ * The PLL management related registers are Reg10h~Reg18h.
++ * @author baozhu (17-6-20)
++ *
++ * @param ac10x
++ * @param rate : sample rate
++ *
++ * @return int : fail or success
++ */
++static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio)
++{
++ unsigned int i = 0;
++ struct pll_div ac108_pll_div = { 0 };
++
++ if (ac10x->clk_id == SYSCLK_SRC_PLL) {
++ unsigned pll_src, pll_freq_in;
++
++ if (lrck_ratio == 0) {
++ /* PLL clock source from MCLK */
++ pll_freq_in = ac10x->sysclk;
++ pll_src = 0x0;
++ } else {
++ /* PLL clock source from BCLK */
++ pll_freq_in = rate * lrck_ratio;
++ pll_src = 0x1;
++ }
++
++ /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */
++ for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) {
++ if (ac108_pll_div_list[i].freq_in == pll_freq_in &&
++ ac108_pll_div_list[i].freq_out % rate == 0) {
++ ac108_pll_div = ac108_pll_div_list[i];
++ dev_info(&ac10x->i2c[_MASTER_INDEX]->dev,
++ "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
++ ac108_pll_div.freq_in, ac108_pll_div.freq_out);
++ break;
++ }
++ }
++ /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */
++ ac108_multi_update_bits(PLL_CTRL5,
++ 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2,
++ ac108_pll_div.k1 << PLL_POSTDIV1 |
++ ac108_pll_div.k2 << PLL_POSTDIV2, ac10x);
++ ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB,
++ (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x);
++ ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB,
++ (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x);
++ ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2,
++ ac108_pll_div.m1 << PLL_PREDIV1 |
++ ac108_pll_div.m2 << PLL_PREDIV2, ac10x);
++
++ /*0x18: PLL clk lock enable*/
++ ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN,
++ 0x1 << PLL_LOCK_EN, ac10x);
++
++ /**
++ * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
++ */
++ ac108_multi_update_bits(SYSCLK_CTRL,
++ 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC |
++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++ 0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC |
++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
++ ac10x->mclk = ac108_pll_div.freq_out;
++ }
++ if (ac10x->clk_id == SYSCLK_SRC_MCLK) {
++ /**
++ *0x20: sysclk source from mclk, enable sysclk
++ */
++ ac108_multi_update_bits(SYSCLK_CTRL,
++ 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++ 0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++ ac10x);
++ ac10x->mclk = ac10x->sysclk;
++ }
++
++ return 0;
++}
++
++/*
++ * support no more than 16 slots.
++ */
++static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots)
++{
++ int i;
++
++ /*
++ * codec0 enable slots 2,3,0,1 when 1 codec
++ *
++ * codec0 enable slots 6,7,0,1 when 2 codec
++ * codec1 enable slots 2,3,4,5
++ *
++ * ...
++ */
++ for (i = 0; i < ac->codec_cnt; i++) {
++ /* rotate map, due to channels rotated by CPU_DAI */
++ const unsigned vec_mask[] = {
++ 0x3 << 6 | 0x3, // slots 6,7,0,1
++ 0xF << 2, // slots 2,3,4,5
++ 0,
++ 0,
++ };
++ const unsigned vec_maps[] = {
++ /*
++ * chip 0,
++ * mic 0 sample -> slot 6
++ * mic 1 sample -> slot 7
++ * mic 2 sample -> slot 0
++ * mic 3 sample -> slot 1
++ */
++ 0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2,
++ /*
++ * chip 1,
++ * mic 0 sample -> slot 2
++ * mic 1 sample -> slot 3
++ * mic 2 sample -> slot 4
++ * mic 3 sample -> slot 5
++ */
++ 0x0 << 4 | 0x1 << 6 | 0x2 << 8 | 0x3 << 10,
++ 0,
++ 0,
++ };
++ unsigned vec;
++
++ /* 0x38-0x3A I2S_TX1_CTRLx */
++ if (ac->codec_cnt == 1) {
++ vec = 0xFUL;
++ } else {
++ vec = vec_mask[i];
++ }
++ ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]);
++ ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]);
++ ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]);
++
++ /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */
++ if (ac->codec_cnt == 1) {
++ vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6);
++ } else if (ac->codec_cnt == 2) {
++ vec = vec_maps[i];
++ }
++
++ ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >> 0) & 0xFF, ac->i2cmap[i]);
++ ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >> 8) & 0xFF, ac->i2cmap[i]);
++ ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]);
++ ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]);
++ }
++ return 0;
++}
++
++static int ac108_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++ unsigned int i, channels, samp_res, rate;
++ struct snd_soc_codec *codec = dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ unsigned bclkdiv;
++ int ret = 0;
++ u8 v;
++
++ dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
++ snd_pcm_stream_str(substream),
++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active);
++
++ if (ac10x->i2c101) {
++ ret = ac101_hw_params(substream, params, dai);
++ if (ret > 0) {
++ dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__);
++ /* not configure hw_param twice */
++ return 0;
++ }
++ }
++
++ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active)
++ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) {
++ /* not configure hw_param twice */
++ /* return 0; */
++ }
++
++ channels = params_channels(params);
++
++ /* Master mode, to clear cpu_dai fifos, output bclk without lrck */
++ ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]);
++ if (v & (0x01 << BCLK_IOEN)) {
++ ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN,
++ 0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
++ }
++
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S8:
++ samp_res = 0;
++ break;
++ case SNDRV_PCM_FORMAT_S16_LE:
++ samp_res = 2;
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ samp_res = 3;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ samp_res = 4;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ samp_res = 6;
++ break;
++ default:
++ dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n",
++ params_format(params));
++ return -EINVAL;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) {
++ if (ac108_sample_rate[i].real_val == params_rate(params) /
++ (ac10x->data_protocol + 1UL)) {
++ rate = i;
++ break;
++ }
++ }
++ if (i >= ARRAY_SIZE(ac108_sample_rate)) {
++ return -EINVAL;
++ }
++
++ if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) {
++ /* 24.576M bit clock is not support by ac108 */
++ return -EINVAL;
++ }
++
++ dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d",
++ ac108_sample_rate[rate].real_val,
++ channels,
++ ac108_samp_res[samp_res].real_val);
++
++ /**
++ * 0x33:
++ * The 8-Low bit of LRCK period value. It is used to program
++ * the number of BCLKs per channel of sample frame. This value
++ * is interpreted as follow:
++ * The 8-Low bit of LRCK period value. It is used to program
++ * the number of BCLKs per channel of sample frame. This value
++ * is interpreted as follow: PCM mode: Number of BCLKs within
++ * (Left + Right) channel width I2S / Left-Justified /
++ * Right-Justified mode: Number of BCLKs within each individual
++ * channel width (Left or Right) N+1
++ * For example:
++ * n = 7: 8 BCLK width
++ * …
++ * n = 1023: 1024 BCLKs width
++ * 0X32[0:1]:
++ * The 2-High bit of LRCK period value.
++ */
++ if (ac10x->i2s_mode != PCM_FORMAT) {
++ if (ac10x->data_protocol) {
++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
++ ac10x);
++ /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/
++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++ } else {
++ /*TDM mode or normal mode*/
++ //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
++ ac10x);
++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++ }
++ } else {
++ unsigned div;
++
++ /*TDM mode or normal mode*/
++ div = ac108_samp_res[samp_res].real_val * channels - 1;
++ ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x);
++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x);
++ }
++
++ /**
++ * 0x35:
++ * TX Encoding mode will add 4bits to mark channel number
++ * TODO: need a chat to explain this
++ */
++ ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL,
++ ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION |
++ ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x);
++
++ /**
++ * 0x60:
++ * ADC Sample Rate synchronised with I2S1 clock zone
++ */
++ ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1,
++ ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x);
++ ac108_multi_write(HPF_EN, 0x0F, ac10x);
++
++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val,
++ ac108_samp_res[samp_res].real_val * channels);
++ } else {
++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0);
++ }
++
++ /*
++ * master mode only
++ */
++ bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels *
++ ac108_samp_res[samp_res].real_val);
++ for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) {
++ if (ac108_bclkdivs[i] >= bclkdiv) {
++ break;
++ }
++ }
++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x);
++
++ /*
++ * slots allocation for each chip
++ */
++ ac108_multi_chips_slots(ac10x, channels);
++
++ /*0x21: Module clock enable<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++ ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL |
++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
++ /*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++ ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL |
++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
++
++ ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x);
++
++ ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x);
++ ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x);
++
++ dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
++ snd_pcm_stream_str(substream));
++
++ return 0;
++}
++
++static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
++{
++
++ struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
++
++ freq = 24000000;
++ clk_id = SYSCLK_SRC_PLL;
++
++ switch (clk_id) {
++ case SYSCLK_SRC_MCLK:
++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
++ SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x);
++ break;
++ case SYSCLK_SRC_PLL:
++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
++ SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x);
++ break;
++ default:
++ return -EINVAL;
++ }
++ ac10x->sysclk = freq;
++ ac10x->clk_id = clk_id;
++
++ return 0;
++}
++
++/**
++ * The i2s format management related registers are Reg
++ * 30h~Reg36h
++ * 33h,35h will be set in ac108_hw_params, It's BCLK width and
++ * Sample Resolution.
++ * @author baozhu (17-6-20)
++ *
++ * @param dai
++ * @param fmt
++ *
++ * @return int
++ */
++static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
++{
++ unsigned char tx_offset, lrck_polarity, brck_polarity;
++ struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev);
++
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM: /* AC108 Master */
++ if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
++ /**
++ * 0x30:chip is master mode ,BCLK & LRCK output
++ */
++ ac108_multi_update_bits(I2S_CTRL,
++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
++ 0x1 << TXEN | 0x1 << GEN,
++ 0x03 << LRCK_IOEN | 0x01 << SDO1_EN |
++ 0x1 << TXEN | 0x1 << GEN, ac10x);
++ /* multi_chips: only one chip set as Master, and the others also need to set as Slave */
++ ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN,
++ ac10x->i2cmap[_MASTER_INDEX]);
++ } else {
++ /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
++ dev_err(dai->dev, "used as slave when AC101 is master\n");
++ }
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS: /* AC108 Slave */
++ /**
++ * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and
++ * SDO2_EN, Transmitter Block Enable, Globe Enable
++ */
++ ac108_multi_update_bits(I2S_CTRL,
++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
++ 0x1 << TXEN | 0x1 << GEN,
++ 0x00 << LRCK_IOEN | 0x03 << SDO1_EN |
++ 0x0 << TXEN | 0x0 << GEN, ac10x);
++ break;
++ default:
++ dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n",
++ (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
++ return -EINVAL;
++ }
++
++ /*AC108 config I2S/LJ/RJ/PCM format*/
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
++ tx_offset = 1;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT;
++ tx_offset = 0;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
++ tx_offset = 0;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ ac10x->i2s_mode = PCM_FORMAT;
++ tx_offset = 1;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ ac10x->i2s_mode = PCM_FORMAT;
++ tx_offset = 0;
++ break;
++ default:
++ dev_err(dai->dev, "AC108 I2S format config error:%u\n\n",
++ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
++ return -EINVAL;
++ }
++
++ /*AC108 config BCLK&LRCK polarity*/
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
++ break;
++ default:
++ dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n",
++ (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
++ return -EINVAL;
++ }
++
++ ac108_configure_power(ac10x);
++
++ /**
++ *0x31: 0: normal mode, negative edge drive and positive edge sample
++ 1: invert mode, positive edge drive and negative edge sample
++ */
++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY,
++ brck_polarity << BCLK_POLARITY, ac10x);
++ /**
++ * 0x32: same as 0x31
++ */
++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY,
++ lrck_polarity << LRCK_POLARITY, ac10x);
++ /**
++ * 0x34:Encoding Mode Selection,Mode
++ * Selection,data is offset by 1 BCLKs to LRCK
++ * normal mode for the last half cycle of BCLK in the slot ?
++ * turn to hi-z state (TDM) when not transferring slot ?
++ */
++ ac108_multi_update_bits(I2S_FMT_CTRL1,
++ 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET |
++ 0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE,
++ ac10x->data_protocol << ENCD_SEL | ac10x->i2s_mode << MODE_SEL |
++ tx_offset << TX2_OFFSET | tx_offset << TX1_OFFSET |
++ 0x00 << TX_SLOT_HIZ | 0x01 << TX_STATE, ac10x);
++
++ /**
++ * 0x60:
++ * MSB / LSB First Select: This driver only support MSB First Select .
++ * OUT2_MUTE,OUT1_MUTE shoule be set in widget.
++ * LRCK = 1 BCLK width
++ * Linear PCM
++ *
++ * TODO:pcm mode, bit[0:1] and bit[2] is special
++ */
++ ac108_multi_update_bits(I2S_FMT_CTRL3,
++ 0x01 << TX_MLS | 0x03 << SEXT |
++ 0x01 << LRCK_WIDTH | 0x03 << TX_PDM,
++ 0x00 << TX_MLS | 0x03 << SEXT |
++ 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x);
++
++ ac108_multi_write(HPF_EN, 0x00, ac10x);
++
++ if (ac10x->i2c101) {
++ return ac101_set_dai_fmt(dai, fmt);
++ }
++ return 0;
++}
++
++/*
++ * due to miss channels order in cpu_dai, we meed defer the clock starting.
++ */
++static int ac108_set_clock(int y_start_n_stop)
++{
++ u8 reg;
++ int ret = 0;
++
++ dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
++
++ /* spin_lock move to machine trigger */
++
++ if (y_start_n_stop && ac10x->sysclk_en == 0) {
++ /* enable lrck clock */
++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
++ if (reg & (0x01 << BCLK_IOEN)) {
++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
++ 0x03 << LRCK_IOEN,
++ ac10x->i2cmap[_MASTER_INDEX]);
++ }
++
++ /*0x10: PLL Common voltage enable, PLL enable */
++ ret = ret || ac108_multi_update_bits(PLL_CTRL1,
++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
++ /* enable global clock */
++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++ 0x1 << TXEN | 0x1 << GEN, ac10x);
++
++ ac10x->sysclk_en = 1UL;
++ } else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
++ /* disable global clock */
++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++ 0x0 << TXEN | 0x0 << GEN, ac10x);
++
++ /*0x10: PLL Common voltage disable, PLL disable */
++ ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
++ 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
++
++ /* disable lrck clock if it's enabled */
++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
++ if (reg & (0x01 << LRCK_IOEN)) {
++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
++ 0x01 << BCLK_IOEN,
++ ac10x->i2cmap[_MASTER_INDEX]);
++ }
++ if (!ret) {
++ ac10x->sysclk_en = 0UL;
++ }
++ }
++
++ return ret;
++}
++
++static int ac108_prepare(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ u8 r;
++
++ dev_dbg(dai->dev, "%s() stream=%s\n",
++ __func__,
++ snd_pcm_stream_str(substream));
++
++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
++ ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai);
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ return 0;
++ }
++ }
++
++ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
++ if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
++ /* disable global clock */
++ ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++ 0x0 << TXEN | 0x0 << GEN, ac10x);
++ }
++
++ /* delayed clock starting, move to machine trigger() */
++ ac108_set_clock(1);
++
++ return 0;
++}
++
++int ac108_audio_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ if (ac10x->i2c101) {
++ return ac101_audio_startup(substream, dai);
++ }
++ return 0;
++}
++
++void ac108_aif_shutdown(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ ac108_set_clock(0);
++
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++ /*0x21: Module clock disable <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++ ac108_multi_write(MOD_CLK_EN, 0x0, ac10x);
++ /*0x22: Module reset asserted <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++ ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x);
++ }
++
++ if (ac10x->i2c101) {
++ ac101_aif_shutdown(substream, dai);
++ }
++}
++
++int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ if (ac10x->i2c101) {
++ return ac101_aif_mute(dai, mute);
++ }
++ return 0;
++}
++
++static const struct snd_soc_dai_ops ac108_dai_ops = {
++ .startup = ac108_audio_startup,
++ .shutdown = ac108_aif_shutdown,
++
++ /*DAI clocking configuration*/
++ .set_sysclk = ac108_set_sysclk,
++
++ /*ALSA PCM audio operations*/
++ .hw_params = ac108_hw_params,
++ .prepare = ac108_prepare,
++ .mute_stream = ac108_aif_mute,
++
++ /*DAI format configuration*/
++ .set_fmt = ac108_set_fmt,
++};
++
++static struct snd_soc_dai_driver ac108_dai0 = {
++ .name = "ac10x-codec0",
++ #if _USE_CAPTURE
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = AC108_CHANNELS_MAX,
++ .rates = AC108_RATES,
++ .formats = AC108_FORMATS,
++ },
++ #endif
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = AC108_CHANNELS_MAX,
++ .rates = AC108_RATES,
++ .formats = AC108_FORMATS,
++ },
++ .ops = &ac108_dai_ops,
++};
++
++static struct snd_soc_dai_driver ac108_dai1 = {
++ .name = "ac10x-codec1",
++ #if _USE_CAPTURE
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = AC108_CHANNELS_MAX,
++ .rates = AC108_RATES,
++ .formats = AC108_FORMATS,
++ },
++ #endif
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = AC108_CHANNELS_MAX,
++ .rates = AC108_RATES,
++ .formats = AC108_FORMATS,
++ },
++ .ops = &ac108_dai_ops,
++};
++
++static struct snd_soc_dai_driver *ac108_dai[] = {
++ &ac108_dai0,
++ &ac108_dai1,
++};
++
++static int ac108_add_widgets(struct snd_soc_codec *codec)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
++ const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls;
++ int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls);
++
++ /* only register controls correspond to exist chips */
++ if (ac10x->tdm_chips_cnt >= 2) {
++ snd_kcntl = ac108tdm_snd_controls;
++ ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls);
++ }
++ snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt);
++
++ snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets));
++ snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes));
++
++ return 0;
++}
++
++static int ac108_codec_probe(struct snd_soc_codec *codec)
++{
++ spin_lock_init(&ac10x->lock);
++
++ ac10x->codec = codec;
++ dev_set_drvdata(codec->dev, ac10x);
++ ac108_add_widgets(codec);
++
++ if (ac10x->i2c101) {
++ ac101_codec_probe(codec);
++ }
++
++ /* change default volume */
++ ac108_multi_update_bits(ADC1_DVOL_CTRL, 0xff, 0xc8, ac10x);
++ ac108_multi_update_bits(ADC2_DVOL_CTRL, 0xff, 0xc8, ac10x);
++ ac108_multi_update_bits(ADC3_DVOL_CTRL, 0xff, 0xc8, ac10x);
++ ac108_multi_update_bits(ADC4_DVOL_CTRL, 0xff, 0xc8, ac10x);
++
++ return 0;
++}
++
++static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ dev_dbg(codec->dev, "AC108 level:%d\n", level);
++
++ switch (level) {
++ case SND_SOC_BIAS_ON:
++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
++ 0x01 << ADC1_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
++ 0x01 << ADC2_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
++ 0x01 << ADC3_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
++ 0x01 << ADC4_MICBIAS_EN, ac10x);
++ break;
++
++ case SND_SOC_BIAS_PREPARE:
++ /* Put the MICBIASes into regulating mode */
++ break;
++
++ case SND_SOC_BIAS_STANDBY:
++ break;
++
++ case SND_SOC_BIAS_OFF:
++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
++ 0x00 << ADC1_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
++ 0x00 << ADC2_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
++ 0x00 << ADC3_MICBIAS_EN, ac10x);
++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
++ 0x00 << ADC4_MICBIAS_EN, ac10x);
++ break;
++ }
++
++ if (ac10x->i2c101) {
++ ac101_set_bias_level(codec, level);
++ }
++ return 0;
++}
++
++int ac108_codec_remove(struct snd_soc_codec *codec) {
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++ if (! ac10x->i2c101) {
++ return 0;
++ }
++ return ac101_codec_remove(codec);
++}
++#if __NO_SND_SOC_CODEC_DRV
++void ac108_codec_remove_void(struct snd_soc_codec *codec)
++{
++ ac108_codec_remove(codec);
++}
++#define ac108_codec_remove ac108_codec_remove_void
++#endif
++
++int ac108_codec_suspend(struct snd_soc_codec *codec)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int i;
++
++ for (i = 0; i < ac10x->codec_cnt; i++) {
++ regcache_cache_only(ac10x->i2cmap[i], true);
++ }
++
++ if (! ac10x->i2c101) {
++ return 0;
++ }
++ return ac101_codec_suspend(codec);
++}
++
++int ac108_codec_resume(struct snd_soc_codec *codec)
++{
++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++ int i, ret;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ac10x->codec_cnt; i++) {
++ regcache_cache_only(ac10x->i2cmap[i], false);
++ ret = regcache_sync(ac10x->i2cmap[i]);
++ if (ret != 0) {
++ dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n",
++ i, ret);
++ regcache_cache_only(ac10x->i2cmap[i], true);
++ }
++ }
++
++ if (! ac10x->i2c101) {
++ return 0;
++ }
++ return ac101_codec_resume(codec);
++}
++
++static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
++ .probe = ac108_codec_probe,
++ .remove = ac108_codec_remove,
++ .suspend = ac108_codec_suspend,
++ .resume = ac108_codec_resume,
++ .set_bias_level = ac108_set_bias_level,
++ .read = ac108_codec_read,
++ .write = ac108_codec_write,
++};
++
++static ssize_t ac108_store(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int val = 0, flag = 0;
++ u8 i = 0, reg, num, value_w, value_r[4];
++
++ val = simple_strtol(buf, NULL, 16);
++ flag = (val >> 16) & 0xF;
++
++ if (flag) {
++ reg = (val >> 8) & 0xFF;
++ value_w = val & 0xFF;
++ ac108_multi_write(reg, value_w, ac10x);
++ dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg);
++ } else {
++ int k;
++
++ reg = (val >> 8) & 0xFF;
++ num = val & 0xff;
++ dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
++
++ for (k = 0; k < ac10x->codec_cnt; k++)
++ regcache_cache_bypass(ac10x->i2cmap[k], true);
++
++ do {
++ memset(value_r, 0, sizeof value_r);
++
++ for (k = 0; k < ac10x->codec_cnt; k++)
++ ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]);
++
++ if (ac10x->codec_cnt >= 2)
++ dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg,
++ value_r[0], value_r[1]);
++ else
++ dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]);
++ reg++;
++
++ if ((++i == num) || (i % 4 == 0))
++ dev_info(dev, "\n");
++ } while (i < num);
++
++ for (k = 0; k < ac10x->codec_cnt; k++)
++ regcache_cache_bypass(ac10x->i2cmap[k], false);
++ }
++
++ return count;
++}
++
++static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ dev_info(dev, "echo flag|reg|val > ac108\n");
++ dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
++ dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
++ return 0;
++}
++
++static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
++static struct attribute *ac108_debug_attrs[] = {
++ &dev_attr_ac108.attr,
++ NULL,
++};
++static struct attribute_group ac108_debug_attr_group = {
++ .name = "ac108_debug",
++ .attrs = ac108_debug_attrs,
++};
++
++static const struct i2c_device_id ac108_i2c_id[] = {
++ { "ac108_0", 0 },
++ { "ac108_1", 1 },
++ { "ac108_2", 2 },
++ { "ac108_3", 3 },
++ { "ac101", AC101_I2C_ID },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
++
++static const struct regmap_config ac108_regmap = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ .reg_stride = 1,
++ .max_register = 0xDF,
++ .cache_type = REGCACHE_FLAT,
++};
++static int ac108_i2c_probe(struct i2c_client *i2c)
++{
++ struct device_node *np = i2c->dev.of_node;
++ const struct i2c_device_id *i2c_id;
++ unsigned int val = 0;
++ int ret = 0, index;
++
++ if (ac10x == NULL) {
++ ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL);
++ if (ac10x == NULL) {
++ dev_err(&i2c->dev, "Unable to allocate ac10x private data\n");
++ return -ENOMEM;
++ }
++ }
++
++ i2c_id = i2c_match_id(ac108_i2c_id, i2c);
++ index = (int)i2c_id->driver_data;
++ if (index == AC101_I2C_ID) {
++ ac10x->i2c101 = i2c;
++ i2c_set_clientdata(i2c, ac10x);
++ ret = ac101_probe(i2c, i2c_id);
++ if (ret) {
++ ac10x->i2c101 = NULL;
++ return ret;
++ }
++ goto __ret;
++ }
++
++ ret = of_property_read_u32(np, "data-protocol", &val);
++ if (ret) {
++ dev_err(&i2c->dev, "Please set data-protocol.\n");
++ return -EINVAL;
++ }
++ ac10x->data_protocol = val;
++
++ if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
++ ac10x->tdm_chips_cnt = val;
++
++ dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index);
++ dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol);
++
++ ac10x->i2c[index] = i2c;
++ ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
++ if (IS_ERR(ac10x->i2cmap[index])) {
++ ret = PTR_ERR(ac10x->i2cmap[index]);
++ dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret);
++ return ret;
++ }
++
++ /*
++ * Writing this register with 0x12
++ * will resets all register to their default state.
++ */
++ regcache_cache_only(ac10x->i2cmap[index], false);
++
++ ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL);
++ msleep(1);
++
++ /* sync regcache for FLAT type */
++ ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
++
++ ac10x->codec_cnt++;
++ dev_info(&i2c->dev, " ac10x codec count : %d\n", ac10x->codec_cnt);
++
++ ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
++ if (ret) {
++ dev_err(&i2c->dev, "failed to create attr group\n");
++ }
++
++__ret:
++ /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */
++ if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
++ || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
++ /* no playback stream */
++ if (! ac10x->i2c101) {
++ memset(&ac108_dai[_MASTER_INDEX]->playback, '\0',
++ sizeof ac108_dai[_MASTER_INDEX]->playback);
++ }
++ ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev,
++ &ac10x_soc_codec_driver,
++ ac108_dai[_MASTER_INDEX], 1);
++ if (ret < 0) {
++ dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
++ }
++ }
++ return ret;
++}
++
++static void ac108_i2c_remove(struct i2c_client *i2c)
++{
++ if (ac10x->codec != NULL) {
++ snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
++ ac10x->codec = NULL;
++ }
++ if (i2c == ac10x->i2c101) {
++ ac101_remove(ac10x->i2c101);
++ ac10x->i2c101 = NULL;
++ goto __ret;
++ }
++
++ if (i2c == ac10x->i2c[0]) {
++ ac10x->i2c[0] = NULL;
++ }
++ if (i2c == ac10x->i2c[1]) {
++ ac10x->i2c[1] = NULL;
++ }
++
++ sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group);
++
++__ret:
++ if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) {
++ kfree(ac10x);
++ ac10x = NULL;
++ }
++}
++
++static const struct of_device_id ac108_of_match[] = {
++ { .compatible = "x-power,ac108_0", },
++ { .compatible = "x-power,ac108_1", },
++ { .compatible = "x-power,ac108_2", },
++ { .compatible = "x-power,ac108_3", },
++ { .compatible = "x-power,ac101", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, ac108_of_match);
++
++static struct i2c_driver ac108_i2c_driver = {
++ .driver = {
++ .name = "ac10x-codec",
++ .of_match_table = ac108_of_match,
++ },
++ .probe = ac108_i2c_probe,
++ .remove = ac108_i2c_remove,
++ .id_table = ac108_i2c_id,
++};
++
++module_i2c_driver(ac108_i2c_driver);
++
++MODULE_DESCRIPTION("ASoC AC108 driver");
++MODULE_AUTHOR("Baozhu Zuo<zuobaozhu@gmail.com>");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/sound/soc/codecs/ac108.h
+@@ -0,0 +1,749 @@
++/*
++ * ac108.h -- ac108 ALSA Soc Audio driver
++ *
++ * Author: panjunwen
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#ifndef _AC108_H
++#define _AC108_H
++
++/*** AC108 Codec Register Define***/
++
++//Chip Reset
++#define CHIP_RST 0x00
++#define CHIP_RST_VAL 0x12
++
++//Power Control
++#define PWR_CTRL1 0x01
++#define PWR_CTRL2 0x02
++#define PWR_CTRL3 0x03
++#define PWR_CTRL4 0x04
++#define PWR_CTRL5 0x05
++#define PWR_CTRL6 0x06
++#define PWR_CTRL7 0x07
++#define PWR_CTRL8 0x08
++#define PWR_CTRL9 0x09
++
++//PLL Configure Control
++#define PLL_CTRL1 0x10
++#define PLL_CTRL2 0x11
++#define PLL_CTRL3 0x12
++#define PLL_CTRL4 0x13
++#define PLL_CTRL5 0x14
++#define PLL_CTRL6 0x16
++#define PLL_CTRL7 0x17
++#define PLL_LOCK_CTRL 0x18
++
++//System Clock Control
++#define SYSCLK_CTRL 0x20
++#define MOD_CLK_EN 0x21
++#define MOD_RST_CTRL 0x22
++#define DSM_CLK_CTRL 0x25
++
++//I2S Common Control
++#define I2S_CTRL 0x30
++#define I2S_BCLK_CTRL 0x31
++#define I2S_LRCK_CTRL1 0x32
++#define I2S_LRCK_CTRL2 0x33
++#define I2S_FMT_CTRL1 0x34
++#define I2S_FMT_CTRL2 0x35
++#define I2S_FMT_CTRL3 0x36
++
++//I2S TX1 Control
++#define I2S_TX1_CTRL1 0x38
++#define I2S_TX1_CTRL2 0x39
++#define I2S_TX1_CTRL3 0x3A
++#define I2S_TX1_CHMP_CTRL1 0x3C
++#define I2S_TX1_CHMP_CTRL2 0x3D
++#define I2S_TX1_CHMP_CTRL3 0x3E
++#define I2S_TX1_CHMP_CTRL4 0x3F
++
++//I2S TX2 Control
++#define I2S_TX2_CTRL1 0x40
++#define I2S_TX2_CTRL2 0x41
++#define I2S_TX2_CTRL3 0x42
++#define I2S_TX2_CHMP_CTRL1 0x44
++#define I2S_TX2_CHMP_CTRL2 0x45
++#define I2S_TX2_CHMP_CTRL3 0x46
++#define I2S_TX2_CHMP_CTRL4 0x47
++
++//I2S RX1 Control
++#define I2S_RX1_CTRL1 0x50
++#define I2S_RX1_CHMP_CTRL1 0x54
++#define I2S_RX1_CHMP_CTRL2 0x55
++#define I2S_RX1_CHMP_CTRL3 0x56
++#define I2S_RX1_CHMP_CTRL4 0x57
++
++//I2S Loopback Debug
++#define I2S_LPB_DEBUG 0x58
++
++//ADC Common Control
++#define ADC_SPRC 0x60
++#define ADC_DIG_EN 0x61
++#define DMIC_EN 0x62
++#define ADC_DSR 0x63
++#define ADC_FIR 0x64
++#define ADC_DDT_CTRL 0x65
++
++//HPF Control
++#define HPF_EN 0x66
++#define HPF_COEF_REGH1 0x67
++#define HPF_COEF_REGH2 0x68
++#define HPF_COEF_REGL1 0x69
++#define HPF_COEF_REGL2 0x6A
++#define HPF_GAIN_REGH1 0x6B
++#define HPF_GAIN_REGH2 0x6C
++#define HPF_GAIN_REGL1 0x6D
++#define HPF_GAIN_REGL2 0x6E
++
++//ADC Digital Channel Volume Control
++#define ADC1_DVOL_CTRL 0x70
++#define ADC2_DVOL_CTRL 0x71
++#define ADC3_DVOL_CTRL 0x72
++#define ADC4_DVOL_CTRL 0x73
++
++//ADC Digital Mixer Source and Gain Control
++#define ADC1_DMIX_SRC 0x76
++#define ADC2_DMIX_SRC 0x77
++#define ADC3_DMIX_SRC 0x78
++#define ADC4_DMIX_SRC 0x79
++
++//ADC Digital Debug Control
++#define ADC_DIG_DEBUG 0x7F
++
++//I2S Pad Drive Control
++#define I2S_DAT_PADDRV_CTRL 0x80
++#define I2S_CLK_PADDRV_CTRL 0x81
++
++//Analog PGA Control
++#define ANA_PGA1_CTRL 0x90
++#define ANA_PGA2_CTRL 0x91
++#define ANA_PGA3_CTRL 0x92
++#define ANA_PGA4_CTRL 0x93
++
++//MIC Offset Control
++#define MIC_OFFSET_CTRL1 0x96
++#define MIC_OFFSET_CTRL2 0x97
++#define MIC1_OFFSET_STATU1 0x98
++#define MIC1_OFFSET_STATU2 0x99
++#define MIC2_OFFSET_STATU1 0x9A
++#define MIC2_OFFSET_STATU2 0x9B
++#define MIC3_OFFSET_STATU1 0x9C
++#define MIC3_OFFSET_STATU2 0x9D
++#define MIC4_OFFSET_STATU1 0x9E
++#define MIC4_OFFSET_STATU2 0x9F
++
++//ADC1 Analog Control
++#define ANA_ADC1_CTRL1 0xA0
++#define ANA_ADC1_CTRL2 0xA1
++#define ANA_ADC1_CTRL3 0xA2
++#define ANA_ADC1_CTRL4 0xA3
++#define ANA_ADC1_CTRL5 0xA4
++#define ANA_ADC1_CTRL6 0xA5
++#define ANA_ADC1_CTRL7 0xA6
++
++//ADC2 Analog Control
++#define ANA_ADC2_CTRL1 0xA7
++#define ANA_ADC2_CTRL2 0xA8
++#define ANA_ADC2_CTRL3 0xA9
++#define ANA_ADC2_CTRL4 0xAA
++#define ANA_ADC2_CTRL5 0xAB
++#define ANA_ADC2_CTRL6 0xAC
++#define ANA_ADC2_CTRL7 0xAD
++
++//ADC3 Analog Control
++#define ANA_ADC3_CTRL1 0xAE
++#define ANA_ADC3_CTRL2 0xAF
++#define ANA_ADC3_CTRL3 0xB0
++#define ANA_ADC3_CTRL4 0xB1
++#define ANA_ADC3_CTRL5 0xB2
++#define ANA_ADC3_CTRL6 0xB3
++#define ANA_ADC3_CTRL7 0xB4
++
++//ADC4 Analog Control
++#define ANA_ADC4_CTRL1 0xB5
++#define ANA_ADC4_CTRL2 0xB6
++#define ANA_ADC4_CTRL3 0xB7
++#define ANA_ADC4_CTRL4 0xB8
++#define ANA_ADC4_CTRL5 0xB9
++#define ANA_ADC4_CTRL6 0xBA
++#define ANA_ADC4_CTRL7 0xBB
++
++//GPIO Configure
++#define GPIO_CFG1 0xC0
++#define GPIO_CFG2 0xC1
++#define GPIO_DAT 0xC2
++#define GPIO_DRV 0xC3
++#define GPIO_PULL 0xC4
++#define GPIO_INT_CFG 0xC5
++#define GPIO_INT_EN 0xC6
++#define GPIO_INT_STATUS 0xC7
++
++//Misc
++#define BGTC_DAT 0xD1
++#define BGVC_DAT 0xD2
++#define PRNG_CLK_CTRL 0xDF
++
++/*** AC108 Codec Register Bit Define***/
++
++/*PWR_CTRL1*/
++#define CP12_CTRL 4
++#define CP12_SENSE_SELECT 3
++
++/*PWR_CTRL2*/
++#define CP12_SENSE_FILT 6
++#define CP12_COMP_FF_EN 3
++#define CP12_FORCE_ENABLE 2
++#define CP12_FORCE_RSTB 1
++
++/*PWR_CTRL3*/
++#define LDO33DIG_CTRL 0
++
++/*PWR_CTRL6*/
++#define LDO33ANA_2XHDRM 2
++#define LDO33ANA_ENABLE 0
++
++/*PWR_CTRL7*/
++#define VREF_SEL 3
++#define VREF_FASTSTART_ENABLE 1
++#define VREF_ENABLE 0
++
++/*PWR_CTRL9*/
++#define VREFP_FASTSTART_ENABLE 7
++#define VREFP_RESCTRL 5
++#define VREFP_LPMODE 4
++#define IGEN_TRIM 1
++#define VREFP_ENABLE 0
++
++/*PLL_CTRL1*/
++#define PLL_IBIAS 4
++#define PLL_NDET 3
++#define PLL_LOCKED_STATUS 2
++#define PLL_COM_EN 1
++#define PLL_EN 0
++
++/*PLL_CTRL2*/
++#define PLL_PREDIV2 5
++#define PLL_PREDIV1 0
++
++/*PLL_CTRL3*/
++#define PLL_LOOPDIV_MSB 0
++
++/*PLL_CTRL4*/
++#define PLL_LOOPDIV_LSB 0
++
++/*PLL_CTRL5*/
++#define PLL_POSTDIV2 5
++#define PLL_POSTDIV1 0
++
++/*PLL_CTRL6*/
++#define PLL_LDO 6
++#define PLL_CP 0
++
++/*PLL_CTRL7*/
++#define PLL_CAP 6
++#define PLL_RES 4
++#define PLL_TEST_EN 0
++
++/*PLL_LOCK_CTRL*/
++#define LOCK_LEVEL1 2
++#define LOCK_LEVEL2 1
++#define PLL_LOCK_EN 0
++
++/*SYSCLK_CTRL*/
++#define PLLCLK_EN 7
++#define PLLCLK_SRC 4
++#define SYSCLK_SRC 3
++#define SYSCLK_EN 0
++
++/*MOD_CLK_EN & MOD_RST_CTRL*/
++#define I2S 7
++#define ADC_DIGITAL 4
++#define MIC_OFFSET_CALIBRATION 1
++#define ADC_ANALOG 0
++
++/*DSM_CLK_CTRL*/
++#define MIC_OFFSET_DIV 4
++#define DSM_CLK_SEL 0
++
++/*I2S_CTRL*/
++#define BCLK_IOEN 7
++#define LRCK_IOEN 6
++#define SDO2_EN 5
++#define SDO1_EN 4
++#define TXEN 2
++#define RXEN 1
++#define GEN 0
++
++/*I2S_BCLK_CTRL*/
++#define EDGE_TRANSFER 5
++#define BCLK_POLARITY 4
++#define BCLKDIV 0
++
++/*I2S_LRCK_CTRL1*/
++#define LRCK_POLARITY 4
++#define LRCK_PERIODH 0
++
++/*I2S_LRCK_CTRL2*/
++#define LRCK_PERIODL 0
++
++/*I2S_FMT_CTRL1*/
++#define ENCD_SEL 6
++#define MODE_SEL 4
++#define TX2_OFFSET 3
++#define TX1_OFFSET 2
++#define TX_SLOT_HIZ 1
++#define TX_STATE 0
++
++/*I2S_FMT_CTRL2*/
++#define SLOT_WIDTH_SEL 4
++#define SAMPLE_RESOLUTION 0
++
++/*I2S_FMT_CTRL3*/
++#define TX_MLS 7
++#define SEXT 5
++#define OUT2_MUTE 4
++#define OUT1_MUTE 3
++#define LRCK_WIDTH 2
++#define TX_PDM 0
++
++/*I2S_TX1_CTRL1*/
++#define TX1_CHSEL 0
++
++/*I2S_TX1_CTRL2*/
++#define TX1_CH8_EN 7
++#define TX1_CH7_EN 6
++#define TX1_CH6_EN 5
++#define TX1_CH5_EN 4
++#define TX1_CH4_EN 3
++#define TX1_CH3_EN 2
++#define TX1_CH2_EN 1
++#define TX1_CH1_EN 0
++
++/*I2S_TX1_CTRL3*/
++#define TX1_CH16_EN 7
++#define TX1_CH15_EN 6
++#define TX1_CH14_EN 5
++#define TX1_CH13_EN 4
++#define TX1_CH12_EN 3
++#define TX1_CH11_EN 2
++#define TX1_CH10_EN 1
++#define TX1_CH9_EN 0
++
++/*I2S_TX1_CHMP_CTRL1*/
++#define TX1_CH4_MAP 6
++#define TX1_CH3_MAP 4
++#define TX1_CH2_MAP 2
++#define TX1_CH1_MAP 0
++
++/*I2S_TX1_CHMP_CTRL2*/
++#define TX1_CH8_MAP 6
++#define TX1_CH7_MAP 4
++#define TX1_CH6_MAP 2
++#define TX1_CH5_MAP 0
++
++/*I2S_TX1_CHMP_CTRL3*/
++#define TX1_CH12_MAP 6
++#define TX1_CH11_MAP 4
++#define TX1_CH10_MAP 2
++#define TX1_CH9_MAP 0
++
++/*I2S_TX1_CHMP_CTRL4*/
++#define TX1_CH16_MAP 6
++#define TX1_CH15_MAP 4
++#define TX1_CH14_MAP 2
++#define TX1_CH13_MAP 0
++
++/*I2S_TX2_CTRL1*/
++#define TX2_CHSEL 0
++
++/*I2S_TX2_CHMP_CTRL1*/
++#define TX2_CH4_MAP 6
++#define TX2_CH3_MAP 4
++#define TX2_CH2_MAP 2
++#define TX2_CH1_MAP 0
++
++/*I2S_TX2_CHMP_CTRL2*/
++#define TX2_CH8_MAP 6
++#define TX2_CH7_MAP 4
++#define TX2_CH6_MAP 2
++#define TX2_CH5_MAP 0
++
++/*I2S_TX2_CHMP_CTRL3*/
++#define TX2_CH12_MAP 6
++#define TX2_CH11_MAP 4
++#define TX2_CH10_MAP 2
++#define TX2_CH9_MAP 0
++
++/*I2S_TX2_CHMP_CTRL4*/
++#define TX2_CH16_MAP 6
++#define TX2_CH15_MAP 4
++#define TX2_CH14_MAP 2
++#define TX2_CH13_MAP 0
++
++/*I2S_RX1_CTRL1*/
++#define RX1_CHSEL 0
++
++/*I2S_RX1_CHMP_CTRL1*/
++#define RX1_CH4_MAP 6
++#define RX1_CH3_MAP 4
++#define RX1_CH2_MAP 2
++#define RX1_CH1_MAP 0
++
++/*I2S_RX1_CHMP_CTRL2*/
++#define RX1_CH8_MAP 6
++#define RX1_CH7_MAP 4
++#define RX1_CH6_MAP 2
++#define RX1_CH5_MAP 0
++
++/*I2S_RX1_CHMP_CTRL3*/
++#define RX1_CH12_MAP 6
++#define RX1_CH11_MAP 4
++#define RX1_CH10_MAP 2
++#define RX1_CH9_MAP 0
++
++/*I2S_RX1_CHMP_CTRL4*/
++#define RX1_CH16_MAP 6
++#define RX1_CH15_MAP 4
++#define RX1_CH14_MAP 2
++#define RX1_CH13_MAP 0
++
++/*I2S_LPB_DEBUG*/
++#define I2S_LPB_DEBUG_EN 0
++
++/*ADC_SPRC*/
++#define ADC_FS_I2S1 0
++
++/*ADC_DIG_EN*/
++#define DG_EN 4
++#define ENAD4 3
++#define ENAD3 2
++#define ENAD2 1
++#define ENAD1 0
++
++/*DMIC_EN*/
++#define DMIC2_EN 1
++#define DMIC1_EN 0
++
++/*ADC_DSR*/
++#define DIG_ADC4_SRS 6
++#define DIG_ADC3_SRS 4
++#define DIG_ADC2_SRS 2
++#define DIG_ADC1_SRS 0
++
++/*ADC_DDT_CTRL*/
++#define ADOUT_DLY_EN 2
++#define ADOUT_DTS 0
++
++/*HPF_EN*/
++#define DIG_ADC4_HPF_EN 3
++#define DIG_ADC3_HPF_EN 2
++#define DIG_ADC2_HPF_EN 1
++#define DIG_ADC1_HPF_EN 0
++
++/*ADC1_DMIX_SRC*/
++#define ADC1_ADC4_DMXL_GC 7
++#define ADC1_ADC3_DMXL_GC 6
++#define ADC1_ADC2_DMXL_GC 5
++#define ADC1_ADC1_DMXL_GC 4
++#define ADC1_ADC4_DMXL_SRC 3
++#define ADC1_ADC3_DMXL_SRC 2
++#define ADC1_ADC2_DMXL_SRC 1
++#define ADC1_ADC1_DMXL_SRC 0
++
++/*ADC2_DMIX_SRC*/
++#define ADC2_ADC4_DMXL_GC 7
++#define ADC2_ADC3_DMXL_GC 6
++#define ADC2_ADC2_DMXL_GC 5
++#define ADC2_ADC1_DMXL_GC 4
++#define ADC2_ADC4_DMXL_SRC 3
++#define ADC2_ADC3_DMXL_SRC 2
++#define ADC2_ADC2_DMXL_SRC 1
++#define ADC2_ADC1_DMXL_SRC 0
++
++/*ADC3_DMIX_SRC*/
++#define ADC3_ADC4_DMXL_GC 7
++#define ADC3_ADC3_DMXL_GC 6
++#define ADC3_ADC2_DMXL_GC 5
++#define ADC3_ADC1_DMXL_GC 4
++#define ADC3_ADC4_DMXL_SRC 3
++#define ADC3_ADC3_DMXL_SRC 2
++#define ADC3_ADC2_DMXL_SRC 1
++#define ADC3_ADC1_DMXL_SRC 0
++
++/*ADC4_DMIX_SRC*/
++#define ADC4_ADC4_DMXL_GC 7
++#define ADC4_ADC3_DMXL_GC 6
++#define ADC4_ADC2_DMXL_GC 5
++#define ADC4_ADC1_DMXL_GC 4
++#define ADC4_ADC4_DMXL_SRC 3
++#define ADC4_ADC3_DMXL_SRC 2
++#define ADC4_ADC2_DMXL_SRC 1
++#define ADC4_ADC1_DMXL_SRC 0
++
++/*ADC_DIG_DEBUG*/
++#define ADC_PTN_SEL 0
++
++/*I2S_DAT_PADDRV_CTRL*/
++#define TX2_DAT_DRV 4
++#define TX1_DAT_DRV 0
++
++/*I2S_CLK_PADDRV_CTRL*/
++#define LRCK_DRV 4
++#define BCLK_DRV 0
++
++/*ANA_PGA1_CTRL*/
++#define ADC1_ANALOG_PGA 1
++#define ADC1_ANALOG_PGA_STEP 0
++
++/*ANA_PGA2_CTRL*/
++#define ADC2_ANALOG_PGA 1
++#define ADC2_ANALOG_PGA_STEP 0
++
++/*ANA_PGA3_CTRL*/
++#define ADC3_ANALOG_PGA 1
++#define ADC3_ANALOG_PGA_STEP 0
++
++/*ANA_PGA4_CTRL*/
++#define ADC4_ANALOG_PGA 1
++#define ADC4_ANALOG_PGA_STEP 0
++
++
++/*MIC_OFFSET_CTRL1*/
++#define MIC_OFFSET_CAL_EN4 3
++#define MIC_OFFSET_CAL_EN3 2
++#define MIC_OFFSET_CAL_EN2 1
++#define MIC_OFFSET_CAL_EN1 0
++
++/*MIC_OFFSET_CTRL2*/
++#define MIC_OFFSET_CAL_GAIN 3
++#define MIC_OFFSET_CAL_CHANNEL 1
++#define MIC_OFFSET_CAL_EN_ONCE 0
++
++/*MIC1_OFFSET_STATU1*/
++#define MIC1_OFFSET_CAL_DONE 7
++#define MIC1_OFFSET_CAL_RUN_STA 6
++#define MIC1_OFFSET_MSB 0
++
++/*MIC1_OFFSET_STATU2*/
++#define MIC1_OFFSET_LSB 0
++
++/*MIC2_OFFSET_STATU1*/
++#define MIC2_OFFSET_CAL_DONE 7
++#define MIC2_OFFSET_CAL_RUN_STA 6
++#define MIC2_OFFSET_MSB 0
++
++/*MIC2_OFFSET_STATU2*/
++#define MIC2_OFFSET_LSB 0
++
++/*MIC3_OFFSET_STATU1*/
++#define MIC3_OFFSET_CAL_DONE 7
++#define MIC3_OFFSET_CAL_RUN_STA 6
++#define MIC3_OFFSET_MSB 0
++
++/*MIC3_OFFSET_STATU2*/
++#define MIC3_OFFSET_LSB 0
++
++/*MIC4_OFFSET_STATU1*/
++#define MIC4_OFFSET_CAL_DONE 7
++#define MIC4_OFFSET_CAL_RUN_STA 6
++#define MIC4_OFFSET_MSB 0
++
++/*MIC4_OFFSET_STATU2*/
++#define MIC4_OFFSET_LSB 0
++
++/*ANA_ADC1_CTRL1*/
++#define ADC1_PGA_BYPASS 7
++#define ADC1_PGA_BYP_RCM 6
++#define ADC1_PGA_CTRL_RCM 4
++#define ADC1_PGA_MUTE 3
++#define ADC1_DSM_ENABLE 2
++#define ADC1_PGA_ENABLE 1
++#define ADC1_MICBIAS_EN 0
++
++/*ANA_ADC1_CTRL3*/
++#define ADC1_ANA_CAL_EN 5
++#define ADC1_SEL_OUT_EDGE 3
++#define ADC1_DSM_DISABLE 2
++#define ADC1_VREFP_DISABLE 1
++#define ADC1_AAF_DISABLE 0
++
++/*ANA_ADC1_CTRL6*/
++#define PGA_CTRL_TC 6
++#define PGA_CTRL_RC 4
++#define PGA_CTRL_I_LIN 2
++#define PGA_CTRL_I_IN 0
++
++/*ANA_ADC1_CTRL7*/
++#define PGA_CTRL_HI_Z 7
++#define PGA_CTRL_SHORT_RF 6
++#define PGA_CTRL_VCM_VG 4
++#define PGA_CTRL_VCM_IN 0
++
++/*ANA_ADC2_CTRL1*/
++#define ADC2_PGA_BYPASS 7
++#define ADC2_PGA_BYP_RCM 6
++#define ADC2_PGA_CTRL_RCM 4
++#define ADC2_PGA_MUTE 3
++#define ADC2_DSM_ENABLE 2
++#define ADC2_PGA_ENABLE 1
++#define ADC2_MICBIAS_EN 0
++
++/*ANA_ADC2_CTRL3*/
++#define ADC2_ANA_CAL_EN 5
++#define ADC2_SEL_OUT_EDGE 3
++#define ADC2_DSM_DISABLE 2
++#define ADC2_VREFP_DISABLE 1
++#define ADC2_AAF_DISABLE 0
++
++/*ANA_ADC2_CTRL6*/
++#define PGA_CTRL_IBOOST 7
++#define PGA_CTRL_IQCTRL 6
++#define PGA_CTRL_OABIAS 4
++#define PGA_CTRL_CMLP_DIS 3
++#define PGA_CTRL_PDB_RIN 2
++#define PGA_CTRL_PEAKDET 0
++
++/*ANA_ADC2_CTRL7*/
++#define AAF_LPMODE_EN 7
++#define AAF_STG2_IB_SEL 4
++#define AAFDSM_IB_DIV2 3
++#define AAF_STG1_IB_SEL 0
++
++/*ANA_ADC3_CTRL1*/
++#define ADC3_PGA_BYPASS 7
++#define ADC3_PGA_BYP_RCM 6
++#define ADC3_PGA_CTRL_RCM 4
++#define ADC3_PGA_MUTE 3
++#define ADC3_DSM_ENABLE 2
++#define ADC3_PGA_ENABLE 1
++#define ADC3_MICBIAS_EN 0
++
++/*ANA_ADC3_CTRL3*/
++#define ADC3_ANA_CAL_EN 5
++#define ADC3_INVERT_CLK 4
++#define ADC3_SEL_OUT_EDGE 3
++#define ADC3_DSM_DISABLE 2
++#define ADC3_VREFP_DISABLE 1
++#define ADC3_AAF_DISABLE 0
++
++/*ANA_ADC3_CTRL7*/
++#define DSM_COMP_IB_SEL 6
++#define DSM_OTA_CTRL 4
++#define DSM_LPMODE 3
++#define DSM_OTA_IB_SEL 0
++
++/*ANA_ADC4_CTRL1*/
++#define ADC4_PGA_BYPASS 7
++#define ADC4_PGA_BYP_RCM 6
++#define ADC4_PGA_CTRL_RCM 4
++#define ADC4_PGA_MUTE 3
++#define ADC4_DSM_ENABLE 2
++#define ADC4_PGA_ENABLE 1
++#define ADC4_MICBIAS_EN 0
++
++/*ANA_ADC4_CTRL3*/
++#define ADC4_ANA_CAL_EN 5
++#define ADC4_SEL_OUT_EDGE 3
++#define ADC4_DSM_DISABLE 2
++#define ADC4_VREFP_DISABLE 1
++#define ADC4_AAF_DISABLE 0
++
++/*ANA_ADC4_CTRL6*/
++#define DSM_DEMOFF 5
++#define DSM_EN_DITHER 4
++#define DSM_VREFP_LPMODE 2
++#define DSM_VREFP_OUTCTRL 0
++
++/*ANA_ADC4_CTRL7*/
++#define CK8M_EN 5
++#define OSC_EN 4
++#define ADC4_CLK_GATING 3
++#define ADC3_CLK_GATING 2
++#define ADC2_CLK_GATING 1
++#define ADC1_CLK_GATING 0
++
++/*GPIO_CFG1*/
++#define GPIO2_SELECT 4
++#define GPIO1_SELECT 0
++
++/*GPIO_CFG2*/
++#define GPIO4_SELECT 4
++#define GPIO3_SELECT 0
++
++/*GPIO_DAT*///order???
++#define GPIO4_DAT 3
++#define GPIO3_DAT 2
++#define GPIO2_DAT 1
++#define GPIO1_DAT 0
++
++/*GPIO_DRV*/
++#define GPIO4_DRV 6
++#define GPIO3_DRV 4
++#define GPIO2_DRV 2
++#define GPIO1_DRV 0
++
++/*GPIO_PULL*/
++#define GPIO4_PULL 6
++#define GPIO3_PULL 4
++#define GPIO2_PULL 2
++#define GPIO1_PULL 0
++
++/*GPIO_INT_CFG*/
++#define GPIO4_EINT_CFG 6
++#define GPIO3_EINT_CFG 4
++#define GPIO2_EINT_CFG 2
++#define GPIO1_EINT_CFG 0
++
++/*GPIO_INT_EN*///order???
++#define GPIO4_EINT_EN 3
++#define GPIO3_EINT_EN 2
++#define GPIO2_EINT_EN 1
++#define GPIO1_EINT_EN 0
++
++/*GPIO_INT_STATUS*///order???
++#define GPIO4_EINT_STA 3
++#define GPIO3_EINT_STA 2
++#define GPIO2_EINT_STA 1
++#define GPIO1_EINT_STA 0
++
++/*PRNG_CLK_CTRL*/
++#define PRNG_CLK_EN 1
++#define PRNG_CLK_POS 0
++
++/*** Some Config Value ***/
++
++//[SYSCLK_CTRL]: PLLCLK_SRC
++#define PLLCLK_SRC_MCLK 0
++#define PLLCLK_SRC_BCLK 1
++#define PLLCLK_SRC_GPIO2 2
++#define PLLCLK_SRC_GPIO3 3
++
++//[SYSCLK_CTRL]: SYSCLK_SRC
++#define SYSCLK_SRC_MCLK 0
++#define SYSCLK_SRC_PLL 1
++
++//I2S BCLK POLARITY Control
++#define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0
++#define BCLK_INVERT_DRIVE_P_SAMPLE_N 1
++
++//I2S LRCK POLARITY Control
++#define LRCK_LEFT_LOW_RIGHT_HIGH 0
++#define LRCK_LEFT_HIGH_RIGHT_LOW 1
++
++//I2S Format Selection
++#define PCM_FORMAT 0
++#define LEFT_JUSTIFIED_FORMAT 1
++#define RIGHT_JUSTIFIED_FORMAT 2
++
++//I2S data protocol types
++
++#define IS_ENCODING_MODE 0
++
++#endif
++
+--- /dev/null
++++ b/sound/soc/codecs/ac10x.h
+@@ -0,0 +1,152 @@
++/*
++ * ac10x.h
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2010-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ * huangxin <huangxin@reuuimllatech.com>
++ *
++ * some simple description for this code
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++#ifndef __AC10X_H__
++#define __AC10X_H__
++
++#define AC101_I2C_ID 4
++#define _MASTER_AC108 0
++#define _MASTER_AC101 1
++#define _MASTER_MULTI_CODEC _MASTER_AC101
++
++/* enable headset detecting & headset button pressing */
++#define CONFIG_AC101_SWITCH_DETECT
++
++/* obsolete */
++#define CONFIG_AC10X_TRIG_LOCK 0
++
++#ifdef AC101_DEBG
++ #define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
++#else
++ #define AC101_DBG(...)
++#endif
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0)
++#define __NO_SND_SOC_CODEC_DRV 1
++#else
++#define __NO_SND_SOC_CODEC_DRV 0
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
++#if __has_attribute(__fallthrough__)
++# define fallthrough __attribute__((__fallthrough__))
++#else
++# define fallthrough do {} while (0) /* fallthrough */
++#endif
++#endif
++
++#if __NO_SND_SOC_CODEC_DRV
++#define codec component
++#define snd_soc_codec snd_soc_component
++#define snd_soc_codec_driver snd_soc_component_driver
++#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata
++#define snd_soc_codec_get_dapm snd_soc_component_get_dapm
++#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
++#define snd_soc_kcontrol_codec snd_soc_kcontrol_component
++#define snd_soc_read snd_soc_component_read32
++#define snd_soc_register_codec snd_soc_register_component
++#define snd_soc_unregister_codec snd_soc_unregister_component
++#define snd_soc_update_bits snd_soc_component_update_bits
++#define snd_soc_write snd_soc_component_write
++#define snd_soc_add_codec_controls snd_soc_add_component_controls
++#endif
++
++
++#ifdef CONFIG_AC101_SWITCH_DETECT
++enum headphone_mode_u {
++ HEADPHONE_IDLE,
++ FOUR_HEADPHONE_PLUGIN,
++ THREE_HEADPHONE_PLUGIN,
++};
++#endif
++
++struct ac10x_priv {
++ struct i2c_client *i2c[4];
++ struct regmap* i2cmap[4];
++ int codec_cnt;
++ unsigned sysclk;
++#define _FREQ_24_576K 24576000
++#define _FREQ_22_579K 22579200
++ unsigned mclk; /* master clock or aif_clock/aclk */
++ int clk_id;
++ unsigned char i2s_mode;
++ unsigned char data_protocol;
++ struct delayed_work dlywork;
++ int tdm_chips_cnt;
++ int sysclk_en;
++
++ /* member for ac101 .begin */
++ struct snd_soc_codec *codec;
++ struct i2c_client *i2c101;
++ struct regmap* regmap101;
++
++ struct mutex dac_mutex;
++ u8 dac_enable;
++ spinlock_t lock;
++ u8 aif1_clken;
++
++ struct work_struct codec_resume;
++ struct gpio_desc* gpiod_spk_amp_gate;
++
++ #ifdef CONFIG_AC101_SWITCH_DETECT
++ struct gpio_desc* gpiod_irq;
++ long irq;
++ volatile int irq_cntr;
++ volatile int pullout_cntr;
++ volatile int state;
++
++ enum headphone_mode_u mode;
++ struct work_struct work_switch;
++ struct work_struct work_clear_irq;
++
++ struct input_dev* inpdev;
++ #endif
++ /* member for ac101 .end */
++};
++
++
++/* AC101 DAI operations */
++int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
++int ac101_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *codec_dai);
++int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai);
++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
++
++/* codec driver specific */
++int ac101_codec_probe(struct snd_soc_codec *codec);
++int ac101_codec_remove(struct snd_soc_codec *codec);
++int ac101_codec_suspend(struct snd_soc_codec *codec);
++int ac101_codec_resume(struct snd_soc_codec *codec);
++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level);
++
++/* i2c device specific */
++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
++void ac101_shutdown(struct i2c_client *i2c);
++int ac101_remove(struct i2c_client *i2c);
++
++int ac10x_fill_regcache(struct device* dev, struct regmap* map);
++
++#endif//__AC10X_H__
--- /dev/null
+From f03b7c834baef87e4f740e10a8bbcbfc57bd985a Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 15 Jun 2023 11:32:50 +0800
+Subject: [PATCH 080/116] ASoC: starfive: Add SPDIF and PCM driver
+
+Add SPDIF and SPDIF-PCM driver for StarFive JH7110.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/starfive/Kconfig | 17 +
+ sound/soc/starfive/Makefile | 5 +
+ sound/soc/starfive/jh7110_spdif.c | 568 ++++++++++++++++++++++++++
+ sound/soc/starfive/jh7110_spdif.h | 196 +++++++++
+ sound/soc/starfive/jh7110_spdif_pcm.c | 339 +++++++++++++++
+ 5 files changed, 1125 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_spdif.c
+ create mode 100644 sound/soc/starfive/jh7110_spdif.h
+ create mode 100644 sound/soc/starfive/jh7110_spdif_pcm.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -16,6 +16,23 @@ config SND_SOC_JH7110_PWMDAC
+ Say Y or M if you want to add support for StarFive JH7110
+ PWM-DAC driver.
+
++config SND_SOC_JH7110_SPDIF
++ tristate "JH7110 SPDIF module"
++ depends on HAVE_CLK && SND_SOC_STARFIVE
++ select SND_SOC_GENERIC_DMAENGINE_PCM
++ select REGMAP_MMIO
++ help
++ Say Y or M if you want to add support for SPDIF driver of StarFive
++ JH7110 SoC.
++
++config SND_SOC_JH7110_SPDIF_PCM
++ bool "PCM PIO extension for JH7110 SPDIF"
++ depends on SND_SOC_JH7110_SPDIF
++ default y if SND_SOC_JH7110_SPDIF
++ help
++ Say Y or N if you want to add a custom ALSA extension that registers
++ a PCM and uses PIO to transfer data.
++
+ config SND_SOC_JH7110_TDM
+ tristate "JH7110 TDM device driver"
+ depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,3 +1,8 @@
+ # StarFive Platform Support
+ obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
++
++obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o
++spdif-y := jh7110_spdif.o
++spdif-$(CONFIG_SND_SOC_JH7110_SPDIF_PCM) += jh7110_spdif_pcm.o
++
+ obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif.c
+@@ -0,0 +1,568 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * SPDIF driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <sound/core.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include "jh7110_spdif.h"
++
++static irqreturn_t spdif_irq_handler(int irq, void *dev_id)
++{
++ struct sf_spdif_dev *dev = dev_id;
++ bool irq_valid = false;
++ unsigned int intr;
++ unsigned int stat;
++
++ regmap_read(dev->regmap, SPDIF_INT_REG, &intr);
++ regmap_read(dev->regmap, SPDIF_STAT_REG, &stat);
++ regmap_update_bits(dev->regmap, SPDIF_CTRL, SPDIF_MASK_ENABLE, 0);
++ regmap_update_bits(dev->regmap, SPDIF_INT_REG, SPDIF_INT_REG_BIT, 0);
++
++ if ((stat & SPDIF_EMPTY_FLAG) || (stat & SPDIF_AEMPTY_FLAG)) {
++ sf_spdif_pcm_push_tx(dev);
++ irq_valid = true;
++ }
++
++ if ((stat & SPDIF_FULL_FLAG) || (stat & SPDIF_AFULL_FLAG)) {
++ sf_spdif_pcm_pop_rx(dev);
++ irq_valid = true;
++ }
++
++ if (stat & SPDIF_PARITY_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_UNDERR_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_OVRERR_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_SYNCERR_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_LOCK_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_BEGIN_FLAG)
++ irq_valid = true;
++
++ if (stat & SPDIF_RIGHT_LEFT)
++ irq_valid = true;
++
++ regmap_update_bits(dev->regmap, SPDIF_CTRL,
++ SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
++
++ if (irq_valid)
++ return IRQ_HANDLED;
++ else
++ return IRQ_NONE;
++}
++
++static int sf_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
++
++ if (tx) {
++ /* tx mode */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_TR_MODE, SPDIF_TR_MODE);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_MASK_FIFO, SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK);
++ } else {
++ /* rx mode */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_TR_MODE, 0);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_MASK_FIFO, SPDIF_FULL_MASK | SPDIF_AFULL_MASK);
++ }
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ /* clock recovery form the SPDIF data stream 0:clk_enable */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CLK_ENABLE, 0);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_ENABLE, SPDIF_ENABLE);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ /* clock recovery form the SPDIF data stream 1:power save mode */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_ENABLE, 0);
++ break;
++ default:
++ dev_err(dai->dev, "%s L.%d cmd:%d\n", __func__, __LINE__, cmd);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int sf_spdif_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++ unsigned int channels = params_channels(params);
++ unsigned int rate = params_rate(params);
++ unsigned int format = params_format(params);
++ unsigned int tsamplerate;
++ unsigned int mclk;
++ unsigned int audio_root;
++ int ret;
++
++ switch (channels) {
++ case 1:
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CHANNEL_MODE, SPDIF_CHANNEL_MODE);
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_DUPLICATE, SPDIF_DUPLICATE);
++ spdif->channels = false;
++ break;
++ case 2:
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CHANNEL_MODE, 0);
++ spdif->channels = true;
++ break;
++ default:
++ dev_err(dai->dev, "invalid channels number\n");
++ return -EINVAL;
++ }
++
++ switch (format) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ case SNDRV_PCM_FORMAT_S24_LE:
++ case SNDRV_PCM_FORMAT_S24_3LE:
++ case SNDRV_PCM_FORMAT_S32_LE:
++ break;
++ default:
++ dev_err(dai->dev, "invalid format\n");
++ return -EINVAL;
++ }
++
++ switch (rate) {
++ case 8000:
++ break;
++ case 11025:
++ audio_root = 148500000;
++ /* 11025 * 512 = 5644800 */
++ /* But now pll2 is 1188m and mclk should be 5711539 closely. */
++ mclk = 5711539;
++ break;
++ case 16000:
++ break;
++ case 22050:
++ audio_root = 148500000;
++ mclk = 11423077;
++ break;
++ default:
++ dev_err(dai->dev, "channel:%d sample rate:%d\n", channels, rate);
++ return -EINVAL;
++ }
++
++ /* use mclk_inner clock from 1188m PLL2 will be better about 11k and 22k*/
++ if ((rate == 11025) || (rate == 22050)) {
++ ret = clk_set_parent(spdif->mclk, spdif->mclk_inner);
++ if (ret) {
++ dev_err(dai->dev,
++ "failed to set parent to mclk_inner ret=%d\n", ret);
++ goto fail_ext;
++ }
++
++ ret = clk_set_rate(spdif->audio_root, audio_root);
++ if (ret) {
++ dev_err(dai->dev, "failed to set audio_root rate :%d\n", ret);
++ goto fail_ext;
++ }
++
++ ret = clk_set_rate(spdif->mclk_inner, mclk);
++ if (ret) {
++ dev_err(dai->dev, "failed to set mclk_inner rate :%d\n", ret);
++ goto fail_ext;
++ }
++
++ mclk = clk_get_rate(spdif->mclk_inner);
++ } else {
++ ret = clk_set_parent(spdif->mclk, spdif->mclk_ext);
++ if (ret) {
++ dev_err(dai->dev,
++ "failed to set parent to mclk_ext ret=%d\n", ret);
++ goto fail_ext;
++ }
++
++ mclk = clk_get_rate(spdif->mclk_ext);
++ }
++
++ /* (FCLK)4096000/128=32000 */
++ tsamplerate = (mclk / 128 + rate / 2) / rate - 1;
++ if (tsamplerate < 3)
++ tsamplerate = 3;
++
++ /* transmission sample rate */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate);
++
++ return 0;
++
++fail_ext:
++ return ret;
++}
++
++static int sf_spdif_clks_get(struct platform_device *pdev,
++ struct sf_spdif_dev *spdif)
++{
++ static struct clk_bulk_data clks[] = {
++ { .id = "apb" }, /* clock-names in dts file */
++ { .id = "core" },
++ { .id = "audroot" },
++ { .id = "mclk_inner"},
++ { .id = "mclk_ext"},
++ { .id = "mclk"},
++ };
++ int ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
++
++ spdif->spdif_apb = clks[0].clk;
++ spdif->spdif_core = clks[1].clk;
++ spdif->audio_root = clks[2].clk;
++ spdif->mclk_inner = clks[3].clk;
++ spdif->mclk_ext = clks[4].clk;
++ spdif->mclk = clks[5].clk;
++
++ return ret;
++}
++
++static int sf_spdif_resets_get(struct platform_device *pdev,
++ struct sf_spdif_dev *spdif)
++{
++ struct reset_control_bulk_data resets[] = {
++ { .id = "apb" },
++ };
++ int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets);
++
++ if (ret)
++ return ret;
++
++ spdif->rst_apb = resets[0].rstc;
++
++ return 0;
++}
++
++static int starfive_spdif_crg_enable(struct sf_spdif_dev *spdif, bool enable)
++{
++ int ret;
++
++ dev_dbg(spdif->dev, "starfive_spdif clk&rst %sable.\n", enable ? "en":"dis");
++ if (enable) {
++ ret = clk_prepare_enable(spdif->spdif_apb);
++ if (ret) {
++ dev_err(spdif->dev, "failed to prepare enable spdif_apb\n");
++ goto failed_apb_clk;
++ }
++
++ ret = clk_prepare_enable(spdif->spdif_core);
++ if (ret) {
++ dev_err(spdif->dev, "failed to prepare enable spdif_core\n");
++ goto failed_core_clk;
++ }
++
++ ret = reset_control_deassert(spdif->rst_apb);
++ if (ret) {
++ dev_err(spdif->dev, "failed to deassert apb\n");
++ goto failed_rst;
++ }
++ } else {
++ clk_disable_unprepare(spdif->spdif_core);
++ clk_disable_unprepare(spdif->spdif_apb);
++ }
++
++ return 0;
++
++failed_rst:
++ clk_disable_unprepare(spdif->spdif_core);
++failed_core_clk:
++ clk_disable_unprepare(spdif->spdif_apb);
++failed_apb_clk:
++ return ret;
++}
++
++static int sf_spdif_dai_probe(struct snd_soc_dai *dai)
++{
++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++
++ pm_runtime_get_sync(spdif->dev);
++
++ /* reset */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0);
++
++ /* clear irq */
++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++ SPDIF_INT_REG_BIT, 0);
++
++ /* power save mode */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++
++ /* power save mode */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE,
++ SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_SETPREAMBB, SPDIF_SETPREAMBB);
++
++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++ BIT8TO20MASK<<SPDIF_PREAMBLEDEL, 0x3<<SPDIF_PREAMBLEDEL);
++
++ regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL,
++ ALLBITMASK, 0x20|(0x20<<SPDIF_AFULL_THRESHOLD));
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_PARITYGEN, SPDIF_PARITYGEN);
++
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
++
++ /* APB access to FIFO enable, disable if use DMA/FIFO */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_USE_FIFO_IF, 0);
++
++ /* two channel */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ SPDIF_CHANNEL_MODE, 0);
++
++ pm_runtime_put_sync(spdif->dev);
++ return 0;
++}
++
++static const struct snd_soc_dai_ops sf_spdif_dai_ops = {
++ .probe = sf_spdif_dai_probe,
++ .trigger = sf_spdif_trigger,
++ .hw_params = sf_spdif_hw_params,
++};
++
++#ifdef CONFIG_PM_SLEEP
++static int spdif_system_suspend(struct device *dev)
++{
++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++ /* save the register value */
++ regmap_read(spdif->regmap, SPDIF_CTRL, &spdif->reg_spdif_ctrl);
++ regmap_read(spdif->regmap, SPDIF_INT_REG, &spdif->reg_spdif_int);
++ regmap_read(spdif->regmap, SPDIF_FIFO_CTRL, &spdif->reg_spdif_fifo_ctrl);
++
++ return pm_runtime_force_suspend(dev);
++}
++
++static int spdif_system_resume(struct device *dev)
++{
++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++ int ret = pm_runtime_force_resume(dev);
++
++ if (ret)
++ return ret;
++
++ /* restore the register value */
++ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++ ALLBITMASK, spdif->reg_spdif_ctrl);
++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++ ALLBITMASK, spdif->reg_spdif_int);
++ regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL,
++ ALLBITMASK, spdif->reg_spdif_fifo_ctrl);
++
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_PM
++static int spdif_runtime_suspend(struct device *dev)
++{
++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++ return starfive_spdif_crg_enable(spdif, false);
++}
++
++static int spdif_runtime_resume(struct device *dev)
++{
++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++ return starfive_spdif_crg_enable(spdif, true);
++}
++#endif
++
++static const struct dev_pm_ops spdif_pm_ops = {
++ SET_RUNTIME_PM_OPS(spdif_runtime_suspend, spdif_runtime_resume, NULL)
++ SET_SYSTEM_SLEEP_PM_OPS(spdif_system_suspend, spdif_system_resume)
++};
++
++#define SF_PCM_RATE_44100_192000 (SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | \
++ SNDRV_PCM_RATE_96000 | \
++ SNDRV_PCM_RATE_192000)
++
++#define SF_PCM_RATE_8000_22050 (SNDRV_PCM_RATE_8000 | \
++ SNDRV_PCM_RATE_11025 | \
++ SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050)
++
++static struct snd_soc_dai_driver sf_spdif_dai = {
++ .name = "spdif",
++ .id = 0,
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = SF_PCM_RATE_8000_22050,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE |
++ SNDRV_PCM_FMTBIT_S24_3LE |
++ SNDRV_PCM_FMTBIT_S32_LE,
++ },
++ .ops = &sf_spdif_dai_ops,
++ .symmetric_rate = 1,
++};
++
++static const struct snd_soc_component_driver sf_spdif_component = {
++ .name = "starfive-spdif",
++};
++
++static const struct regmap_config sf_spdif_regmap_config = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = 0x200,
++};
++
++static int sf_spdif_probe(struct platform_device *pdev)
++{
++ struct sf_spdif_dev *spdif;
++ struct resource *res;
++ void __iomem *base;
++ int ret;
++ int irq;
++
++ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
++ if (!spdif)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, spdif);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ spdif->spdif_base = base;
++ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, spdif->spdif_base,
++ &sf_spdif_regmap_config);
++ if (IS_ERR(spdif->regmap))
++ return PTR_ERR(spdif->regmap);
++
++ spdif->dev = &pdev->dev;
++
++ ret = sf_spdif_clks_get(pdev, spdif);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to get audio clock\n");
++ return ret;
++ }
++
++ ret = sf_spdif_resets_get(pdev, spdif);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to get audio reset controls\n");
++ return ret;
++ }
++
++ ret = starfive_spdif_crg_enable(spdif, true);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to enable audio clock\n");
++ return ret;
++ }
++
++ spdif->fifo_th = 16;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq >= 0) {
++ ret = devm_request_irq(&pdev->dev, irq, spdif_irq_handler, 0,
++ pdev->name, spdif);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to request irq\n");
++ return ret;
++ }
++ }
++
++ ret = devm_snd_soc_register_component(&pdev->dev, &sf_spdif_component,
++ &sf_spdif_dai, 1);
++ if (ret)
++ goto err_clk_disable;
++
++ if (irq >= 0) {
++ ret = sf_spdif_pcm_register(pdev);
++ spdif->use_pio = true;
++ } else {
++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
++ 0);
++ spdif->use_pio = false;
++ }
++
++ if (ret)
++ goto err_clk_disable;
++
++ starfive_spdif_crg_enable(spdif, false);
++ pm_runtime_enable(&pdev->dev);
++ dev_dbg(&pdev->dev, "spdif register done.\n");
++
++ return 0;
++
++err_clk_disable:
++ return ret;
++}
++
++static const struct of_device_id sf_spdif_of_match[] = {
++ { .compatible = "starfive,jh7110-spdif", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, sf_spdif_of_match);
++
++static struct platform_driver sf_spdif_driver = {
++ .driver = {
++ .name = "starfive-spdif",
++ .of_match_table = sf_spdif_of_match,
++ .pm = &spdif_pm_ops,
++ },
++ .probe = sf_spdif_probe,
++};
++module_platform_driver(sf_spdif_driver);
++
++MODULE_AUTHOR("curry.zhang <curry.zhang@starfive.com>");
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_DESCRIPTION("starfive SPDIF driver");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif.h
+@@ -0,0 +1,196 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * SPDIF driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#ifndef __SND_SOC_JH7110_SPDIF_H
++#define __SND_SOC_JH7110_SPDIF_H
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/pcm.h>
++
++#define SPDIF_CTRL 0x0
++#define SPDIF_INT_REG 0x4
++#define SPDIF_FIFO_CTRL 0x8
++#define SPDIF_STAT_REG 0xC
++
++#define SPDIF_FIFO_ADDR 0x100
++#define DMAC_SPDIF_POLLING_LEN 256
++
++/* ctrl: sampled on the rising clock edge */
++#define SPDIF_TSAMPLERATE 0 /* [SRATEW-1:0] */
++/* 0:SFR reg reset to defualt value; auto set back to '1' after reset */
++#define SPDIF_SFR_ENABLE (1<<8)
++/* 0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module */
++#define SPDIF_ENABLE (1<<9)
++/* 0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to 1 */
++#define SPDIF_FIFO_ENABLE (1<<10)
++/* 1:blocked and the modules are in power save mode; 0:block feeds the modules */
++#define SPDIF_CLK_ENABLE (1<<11)
++#define SPDIF_TR_MODE (1<<12) /* 0:rx; 1:tx */
++/* 0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error */
++#define SPDIF_PARITCHECK (1<<13)
++/*
++ * 0:parity bit from FIFO is transmitted in sub-frame;
++ * 1:parity bit generated inside the core and added to a transmitted sub-frame
++ */
++#define SPDIF_PARITYGEN (1<<14)
++/* 0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked */
++#define SPDIF_VALIDITYCHECK (1<<15)
++#define SPDIF_CHANNEL_MODE (1<<16) /* 0:two-channel; 1:single-channel */
++/* only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel */
++#define SPDIF_DUPLICATE (1<<17)
++/*
++ * only tx;
++ * 0:first preamble B after reset tx valid sub-frame;
++ * 1:first preamble B is tx after preambleddel(INT_REG)
++ */
++#define SPDIF_SETPREAMBB (1<<18)
++/* 0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable; */
++#define SPDIF_USE_FIFO_IF (1<<19)
++#define SPDIF_PARITY_MASK (1<<21)
++#define SPDIF_UNDERR_MASK (1<<22)
++#define SPDIF_OVRERR_MASK (1<<23)
++#define SPDIF_EMPTY_MASK (1<<24)
++#define SPDIF_AEMPTY_MASK (1<<25)
++#define SPDIF_FULL_MASK (1<<26)
++#define SPDIF_AFULL_MASK (1<<27)
++#define SPDIF_SYNCERR_MASK (1<<28)
++#define SPDIF_LOCK_MASK (1<<29)
++#define SPDIF_BEGIN_MASK (1<<30)
++#define SPDIF_INTEREQ_MAKS (1<<31)
++
++#define SPDIF_MASK_ENABLE (SPDIF_PARITY_MASK | SPDIF_UNDERR_MASK | \
++ SPDIF_OVRERR_MASK | SPDIF_EMPTY_MASK | \
++ SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | \
++ SPDIF_AFULL_MASK | SPDIF_SYNCERR_MASK | \
++ SPDIF_LOCK_MASK | SPDIF_BEGIN_MASK | \
++ SPDIF_INTEREQ_MAKS)
++
++#define SPDIF_MASK_FIFO (SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK | \
++ SPDIF_FULL_MASK | SPDIF_AFULL_MASK)
++
++/* INT_REG */
++#define SPDIF_RSAMPLERATE 0 /* [SRATEW-1:0] */
++#define SPDIF_PREAMBLEDEL 8 /* [PDELAYW+7:8] first B delay */
++#define SPDIF_PARITYO (1<<21) /* 0:clear parity error */
++#define SPDIF_TDATA_UNDERR (1<<22) /* tx data underrun error;0:clear */
++#define SPDIF_RDATA_OVRERR (1<<23) /* rx data overrun error; 0:clear */
++#define SPDIF_FIFO_EMPTY (1<<24) /* empty; 0:clear */
++#define SPDIF_FIOF_AEMPTY (1<<25) /* almost empty; 0:clear */
++#define SPDIF_FIFO_FULL (1<<26) /* FIFO full; 0:clear */
++#define SPDIF_FIFO_AFULL (1<<27) /* FIFO almost full; 0:clear */
++#define SPDIF_SYNCERR (1<<28) /* sync error; 0:clear */
++#define SPDIF_LOCK (1<<29) /* sync; 0:clear */
++#define SPDIF_BLOCK_BEGIN (1<<30) /* new start block rx data */
++
++#define SPDIF_INT_REG_BIT (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | \
++ SPDIF_RDATA_OVRERR | SPDIF_FIFO_EMPTY | \
++ SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | \
++ SPDIF_FIFO_AFULL | SPDIF_SYNCERR | \
++ SPDIF_LOCK | SPDIF_BLOCK_BEGIN)
++
++#define SPDIF_ERROR_INT_STATUS (SPDIF_PARITYO | \
++ SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR)
++#define SPDIF_FIFO_INT_STATUS (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | \
++ SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL)
++
++#define SPDIF_INT_PARITY_ERROR (-1)
++#define SPDIF_INT_TDATA_UNDERR (-2)
++#define SPDIF_INT_RDATA_OVRERR (-3)
++#define SPDIF_INT_FIFO_EMPTY 1
++#define SPDIF_INT_FIFO_AEMPTY 2
++#define SPDIF_INT_FIFO_FULL 3
++#define SPDIF_INT_FIFO_AFULL 4
++#define SPDIF_INT_SYNCERR (-4)
++#define SPDIF_INT_LOCK 5 /* reciever has become synchronized with input data stream */
++#define SPDIF_INT_BLOCK_BEGIN 6 /* start a new block in recieve data, written into FIFO */
++
++/* FIFO_CTRL */
++#define SPDIF_AEMPTY_THRESHOLD 0 /* [depth-1:0] */
++#define SPDIF_AFULL_THRESHOLD 16 /* [depth+15:16] */
++
++/* STAT_REG */
++#define SPDIF_FIFO_LEVEL (1<<0)
++#define SPDIF_PARITY_FLAG (1<<21) /* 1:error; 0:repeated */
++#define SPDIF_UNDERR_FLAG (1<<22) /* 1:error */
++#define SPDIF_OVRERR_FLAG (1<<23) /* 1:error */
++#define SPDIF_EMPTY_FLAG (1<<24) /* 1:fifo empty */
++#define SPDIF_AEMPTY_FLAG (1<<25) /* 1:fifo almost empty */
++#define SPDIF_FULL_FLAG (1<<26) /* 1:fifo full */
++#define SPDIF_AFULL_FLAG (1<<27) /* 1:fifo almost full */
++#define SPDIF_SYNCERR_FLAG (1<<28) /* 1:rx sync error */
++#define SPDIF_LOCK_FLAG (1<<29) /* 1:RX sync */
++#define SPDIF_BEGIN_FLAG (1<<30) /* 1:start a new block */
++/* 1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO */
++#define SPDIF_RIGHT_LEFT (1<<31)
++
++#define BIT8TO20MASK 0x1FFF
++#define ALLBITMASK 0xFFFFFFFF
++
++#define SPDIF_STAT (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | \
++ SPDIF_OVRERR_FLAG | SPDIF_EMPTY_FLAG | \
++ SPDIF_AEMPTY_FLAG | SPDIF_FULL_FLAG | \
++ SPDIF_AFULL_FLAG | SPDIF_SYNCERR_FLAG | \
++ SPDIF_LOCK_FLAG | SPDIF_BEGIN_FLAG | \
++ SPDIF_RIGHT_LEFT)
++struct sf_spdif_dev {
++ void __iomem *spdif_base;
++ struct regmap *regmap;
++ struct device *dev;
++ u32 fifo_th;
++ int active;
++
++ /* data related to DMA transfers b/w i2s and DMAC */
++ struct snd_dmaengine_dai_dma_data play_dma_data;
++ struct snd_dmaengine_dai_dma_data capture_dma_data;
++
++ bool use_pio;
++ struct snd_pcm_substream __rcu *tx_substream;
++ struct snd_pcm_substream __rcu *rx_substream;
++
++ unsigned int (*tx_fn)(struct sf_spdif_dev *dev,
++ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
++ bool *period_elapsed, snd_pcm_format_t format);
++ unsigned int (*rx_fn)(struct sf_spdif_dev *dev,
++ struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
++ bool *period_elapsed, snd_pcm_format_t format);
++
++ snd_pcm_format_t format;
++ bool channels;
++ unsigned int tx_ptr;
++ unsigned int rx_ptr;
++ struct clk *spdif_apb;
++ struct clk *spdif_core;
++ struct clk *audio_root;
++ struct clk *mclk_inner;
++ struct clk *mclk;
++ struct clk *mclk_ext;
++ struct reset_control *rst_apb;
++ unsigned int reg_spdif_ctrl;
++ unsigned int reg_spdif_int;
++ unsigned int reg_spdif_fifo_ctrl;
++
++ struct snd_dmaengine_dai_dma_data dma_data;
++};
++
++#if IS_ENABLED(CONFIG_SND_SOC_JH7110_SPDIF_PCM)
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev);
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev);
++int sf_spdif_pcm_register(struct platform_device *pdev);
++#else
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) { }
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) { }
++int sf_spdif_pcm_register(struct platform_device *pdev)
++{
++ return -EINVAL;
++}
++#endif
++
++#endif /* __SND_SOC_JH7110_SPDIF_H */
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif_pcm.c
+@@ -0,0 +1,339 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * SPDIF PCM driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/io.h>
++#include <linux/rcupdate.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++
++#include "jh7110_spdif.h"
++
++#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN)
++#define PERIOD_BYTES_MIN 4096
++#define PERIODS_MIN 2
++
++static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
++ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
++ bool *period_elapsed, snd_pcm_format_t format)
++{
++ unsigned int period_pos = tx_ptr % runtime->period_size;
++ u32 data[2];
++ int i;
++
++ /* two- channel and signal-channel mode */
++ if (dev->channels) {
++ const u16 (*p16)[2] = (void *)runtime->dma_area;
++ const u32 (*p32)[2] = (void *)runtime->dma_area;
++
++ for (i = 0; i < dev->fifo_th; i++) {
++ if (format == SNDRV_PCM_FORMAT_S16_LE) {
++ data[0] = p16[tx_ptr][0];
++ data[0] = data[0]<<8;
++ data[0] &= 0x00ffff00;
++ data[1] = p16[tx_ptr][1];
++ data[1] = data[1]<<8;
++ data[1] &= 0x00ffff00;
++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
++ data[0] = p32[tx_ptr][0];
++ data[1] = p32[tx_ptr][1];
++
++ /*
++ * To adapt S24_3LE and ALSA pass parameter of S24_LE.
++ * operation of S24_LE should be same to S24_3LE.
++ * So it would wrong when playback S24_LE file.
++ * when want to playback S24_LE file, should add in there:
++ * data[0] = data[0]>>8;
++ * data[1] = data[1]>>8;
++ */
++
++ data[0] &= 0x00ffffff;
++ data[1] &= 0x00ffffff;
++ } else if (format == SNDRV_PCM_FORMAT_S24_3LE) {
++ data[0] = p32[tx_ptr][0];
++ data[1] = p32[tx_ptr][1];
++ data[0] &= 0x00ffffff;
++ data[1] &= 0x00ffffff;
++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++ data[0] = p32[tx_ptr][0];
++ data[0] = data[0]>>8;
++ data[1] = p32[tx_ptr][1];
++ data[1] = data[1]>>8;
++ }
++
++ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
++ iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
++ period_pos++;
++ if (++tx_ptr >= runtime->buffer_size)
++ tx_ptr = 0;
++ }
++ } else {
++ const u16 (*p16) = (void *)runtime->dma_area;
++ const u32 (*p32) = (void *)runtime->dma_area;
++
++ for (i = 0; i < dev->fifo_th; i++) {
++ if (format == SNDRV_PCM_FORMAT_S16_LE) {
++ data[0] = p16[tx_ptr];
++ data[0] = data[0]<<8;
++ data[0] &= 0x00ffff00;
++ } else if (format == SNDRV_PCM_FORMAT_S24_LE ||
++ format == SNDRV_PCM_FORMAT_S24_3LE) {
++ data[0] = p32[tx_ptr];
++ data[0] &= 0x00ffffff;
++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++ data[0] = p32[tx_ptr];
++ data[0] = data[0]>>8;
++ }
++
++ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
++ period_pos++;
++ if (++tx_ptr >= runtime->buffer_size)
++ tx_ptr = 0;
++ }
++ }
++
++ *period_elapsed = period_pos >= runtime->period_size;
++ return tx_ptr;
++}
++
++static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
++ struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
++ bool *period_elapsed, snd_pcm_format_t format)
++{
++ u16 (*p16)[2] = (void *)runtime->dma_area;
++ u32 (*p32)[2] = (void *)runtime->dma_area;
++ unsigned int period_pos = rx_ptr % runtime->period_size;
++ u32 data[2];
++ int i;
++
++ for (i = 0; i < dev->fifo_th; i++) {
++ data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
++ data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
++ if (format == SNDRV_PCM_FORMAT_S16_LE) {
++ p16[rx_ptr][0] = data[0]>>8;
++ p16[rx_ptr][1] = data[1]>>8;
++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
++ p32[rx_ptr][0] = data[0];
++ p32[rx_ptr][1] = data[1];
++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++ p32[rx_ptr][0] = data[0]<<8;
++ p32[rx_ptr][1] = data[1]<<8;
++ }
++
++ period_pos++;
++ if (++rx_ptr >= runtime->buffer_size)
++ rx_ptr = 0;
++ }
++
++ *period_elapsed = period_pos >= runtime->period_size;
++ return rx_ptr;
++}
++
++static const struct snd_pcm_hardware sf_pcm_hardware = {
++ .info = SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME,
++ .rates = SNDRV_PCM_RATE_8000 |
++ SNDRV_PCM_RATE_11025 |
++ SNDRV_PCM_RATE_16000 |
++ SNDRV_PCM_RATE_22050 |
++ SNDRV_PCM_RATE_32000 |
++ SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000,
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE |
++ SNDRV_PCM_FMTBIT_S24_3LE |
++ SNDRV_PCM_FMTBIT_S32_LE,
++ .channels_min = 1,
++ .channels_max = 2,
++ .buffer_bytes_max = BUFFER_BYTES_MAX,
++ .period_bytes_min = PERIOD_BYTES_MIN,
++ .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
++ .periods_min = PERIODS_MIN,
++ .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
++ .fifo_size = 16,
++};
++
++static void sf_spdif_pcm_transfer(struct sf_spdif_dev *dev, bool push)
++{
++ struct snd_pcm_substream *substream;
++ bool active, period_elapsed;
++
++ rcu_read_lock();
++ if (push)
++ substream = rcu_dereference(dev->tx_substream);
++ else
++ substream = rcu_dereference(dev->rx_substream);
++
++ active = substream && snd_pcm_running(substream);
++ if (active) {
++ unsigned int ptr;
++ unsigned int new_ptr;
++
++ if (push) {
++ ptr = READ_ONCE(dev->tx_ptr);
++ new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
++ &period_elapsed, dev->format);
++ cmpxchg(&dev->tx_ptr, ptr, new_ptr);
++ } else {
++ ptr = READ_ONCE(dev->rx_ptr);
++ new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
++ &period_elapsed, dev->format);
++ cmpxchg(&dev->rx_ptr, ptr, new_ptr);
++ }
++
++ if (period_elapsed)
++ snd_pcm_period_elapsed(substream);
++ }
++ rcu_read_unlock();
++}
++
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev)
++{
++ sf_spdif_pcm_transfer(dev, true);
++}
++
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev)
++{
++ sf_spdif_pcm_transfer(dev, false);
++}
++
++static int sf_pcm_open(struct snd_soc_component *component,
++ struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++ struct sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
++
++ snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware);
++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
++ runtime->private_data = dev;
++
++ return 0;
++}
++
++static int sf_pcm_close(struct snd_soc_component *component,
++ struct snd_pcm_substream *substream)
++{
++ synchronize_rcu();
++ return 0;
++}
++
++static int sf_pcm_hw_params(struct snd_soc_component *component,
++ struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct sf_spdif_dev *dev = runtime->private_data;
++
++ switch (params_channels(hw_params)) {
++ case 1:
++ case 2:
++ break;
++ default:
++ dev_err(dev->dev, "invalid channels number\n");
++ return -EINVAL;
++ }
++
++ dev->format = params_format(hw_params);
++ switch (dev->format) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ case SNDRV_PCM_FORMAT_S24_LE:
++ case SNDRV_PCM_FORMAT_S24_3LE:
++ case SNDRV_PCM_FORMAT_S32_LE:
++ break;
++ default:
++ dev_err(dev->dev, "invalid format\n");
++ return -EINVAL;
++ }
++
++ dev->tx_fn = sf_spdif_pcm_tx;
++ dev->rx_fn = sf_spdif_pcm_rx;
++
++ return 0;
++}
++
++static int sf_pcm_trigger(struct snd_soc_component *component,
++ struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct sf_spdif_dev *dev = runtime->private_data;
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ WRITE_ONCE(dev->tx_ptr, 0);
++ rcu_assign_pointer(dev->tx_substream, substream);
++ } else {
++ WRITE_ONCE(dev->rx_ptr, 0);
++ rcu_assign_pointer(dev->rx_substream, substream);
++ }
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ rcu_assign_pointer(dev->tx_substream, NULL);
++ else
++ rcu_assign_pointer(dev->rx_substream, NULL);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component,
++ struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct sf_spdif_dev *dev = runtime->private_data;
++ snd_pcm_uframes_t pos;
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ pos = READ_ONCE(dev->tx_ptr);
++ else
++ pos = READ_ONCE(dev->rx_ptr);
++
++ return pos < runtime->buffer_size ? pos : 0;
++}
++
++static int sf_pcm_new(struct snd_soc_component *component,
++ struct snd_soc_pcm_runtime *rtd)
++{
++ size_t size = sf_pcm_hardware.buffer_bytes_max;
++
++ snd_pcm_set_managed_buffer_all(rtd->pcm,
++ SNDRV_DMA_TYPE_CONTINUOUS,
++ NULL, size, size);
++
++ return 0;
++}
++
++static const struct snd_soc_component_driver sf_pcm_component = {
++ .open = sf_pcm_open,
++ .close = sf_pcm_close,
++ .hw_params = sf_pcm_hw_params,
++ .trigger = sf_pcm_trigger,
++ .pointer = sf_pcm_pointer,
++ .pcm_construct = sf_pcm_new,
++};
++
++int sf_spdif_pcm_register(struct platform_device *pdev)
++{
++ return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component,
++ NULL, 0);
++}
--- /dev/null
+From 9c4858f9fe4d8f8fe5cf347b3ca727016b7ba492 Mon Sep 17 00:00:00 2001
+From: Walker Chen <walker.chen@starfivetech.com>
+Date: Tue, 20 Jun 2023 15:57:53 +0800
+Subject: [PATCH 081/116] ASoC: starfive: Add JH7110 PDM driver
+
+Add pdm driver support for the StarFive JH7110 SoC.
+
+Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
+---
+ sound/soc/starfive/Kconfig | 8 +
+ sound/soc/starfive/Makefile | 2 +
+ sound/soc/starfive/jh7110_pdm.c | 493 ++++++++++++++++++++++++++++++++
+ 3 files changed, 503 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_pdm.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -7,6 +7,14 @@ config SND_SOC_STARFIVE
+ the Starfive SoCs' Audio interfaces. You will also need to
+ select the audio interfaces to support below.
+
++config SND_SOC_JH7110_PDM
++ tristate "JH7110 PDM device driver"
++ depends on HAVE_CLK && SND_SOC_STARFIVE
++ select SND_SOC_JH7110_I2S
++ select REGMAP_MMIO
++ help
++ Say Y or M if you want to add support for StarFive pdm driver.
++
+ config SND_SOC_JH7110_PWMDAC
+ tristate "JH7110 PWM-DAC device driver"
+ depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,4 +1,6 @@
+ # StarFive Platform Support
++obj-$(CONFIG_SND_SOC_JH7110_PDM) += jh7110_pdm.o
++
+ obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
+
+ obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_pdm.c
+@@ -0,0 +1,493 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PDM driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/reset.h>
++#include <linux/module.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
++#include <linux/regmap.h>
++#include <linux/pm_runtime.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dai.h>
++#include <sound/tlv.h>
++
++#define PDM_DMIC_CTRL0 0x00
++#define PDM_DC_SCALE0 0x04
++#define PDM_DMIC_CTRL1 0x10
++#define PDM_DC_SCALE1 0x14
++
++/* PDM CTRL OFFSET */
++#define PDM_DMIC_MSB_SHIFT 1
++#define PDM_DMIC_MSB_MASK (0x7 << PDM_DMIC_MSB_SHIFT)
++#define PDM_DMIC_VOL_SHIFT 16
++#define PDM_DMIC_VOL_MASK (0x3f << PDM_DMIC_VOL_SHIFT)
++#define PDM_VOL_DB_MUTE (0x3f << PDM_DMIC_VOL_SHIFT)
++#define PDM_VOL_DB_MAX 0
++
++#define PDM_DMIC_RVOL_MASK BIT(22)
++#define PDM_DMIC_LVOL_MASK BIT(23)
++#define PDM_DMIC_I2S_SLAVE BIT(24)
++#define PDM_DMIC_HPF_EN BIT(28)
++#define PDM_DMIC_FASTMODE_MASK BIT(29)
++#define PDM_DMIC_DC_BYPASS_MASK BIT(30)
++#define PDM_SW_RST_MASK BIT(31)
++#define PDM_SW_RST_RELEASE BIT(31)
++
++/* PDM SCALE OFFSET */
++#define DMIC_DCOFF3_SHIFT 24
++#define DMIC_DCOFF2_SHIFT 16
++#define DMIC_DCOFF1_SHIFT 8
++
++#define DMIC_DCOFF3_MASK (0xf << DMIC_DCOFF3_SHIFT)
++#define DMIC_DCOFF3_VAL (0xc << DMIC_DCOFF3_SHIFT)
++#define DMIC_DCOFF1_MASK (0xff << DMIC_DCOFF1_SHIFT)
++#define DMIC_DCOFF1_VAL (0x5 << DMIC_DCOFF1_SHIFT)
++#define DMIC_SCALE_MASK 0x3f
++#define DMIC_SCALE_DEF_VAL 0x8
++
++enum PDM_MSB_SHIFT {
++ PDM_MSB_SHIFT_NONE = 0,
++ PDM_MSB_SHIFT_1,
++ PDM_MSB_SHIFT_2,
++ PDM_MSB_SHIFT_3,
++ PDM_MSB_SHIFT_4,
++ PDM_MSB_SHIFT_5,
++ PDM_MSB_SHIFT_6,
++ PDM_MSB_SHIFT_7,
++};
++
++struct sf_pdm {
++ struct regmap *pdm_map;
++ struct device *dev;
++ struct clk *clk_pdm_apb;
++ struct clk *clk_pdm_mclk;
++ struct clk *clk_mclk;
++ struct clk *clk_mclk_ext;
++ struct reset_control *rst_pdm_dmic;
++ struct reset_control *rst_pdm_apb;
++ unsigned char flag_first;
++ unsigned int saved_ctrl0;
++ unsigned int saved_scale0;
++};
++
++static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0);
++
++static const struct snd_kcontrol_new sf_pdm_snd_controls[] = {
++ SOC_SINGLE("DC compensation Control", PDM_DMIC_CTRL0, 30, 1, 0),
++ SOC_SINGLE("High Pass Filter Control", PDM_DMIC_CTRL0, 28, 1, 0),
++ SOC_SINGLE("Left Channel Volume Control", PDM_DMIC_CTRL0, 23, 1, 0),
++ SOC_SINGLE("Right Channel Volume Control", PDM_DMIC_CTRL0, 22, 1, 0),
++ SOC_SINGLE_TLV("Volume", PDM_DMIC_CTRL0, 16, 0x3F, 1, volume_tlv),
++ SOC_SINGLE("Data MSB Shift", PDM_DMIC_CTRL0, 1, 7, 0),
++ SOC_SINGLE("SCALE", PDM_DC_SCALE0, 0, 0x3F, 0),
++ SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0),
++};
++
++static void sf_pdm_enable(struct regmap *map)
++{
++ /* Left and Right Channel Volume Control Enable */
++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_RVOL_MASK, 0);
++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_LVOL_MASK, 0);
++}
++
++static void sf_pdm_disable(struct regmap *map)
++{
++ /* Left and Right Channel Volume Control Disable */
++ regmap_update_bits(map, PDM_DMIC_CTRL0,
++ PDM_DMIC_RVOL_MASK, PDM_DMIC_RVOL_MASK);
++ regmap_update_bits(map, PDM_DMIC_CTRL0,
++ PDM_DMIC_LVOL_MASK, PDM_DMIC_LVOL_MASK);
++}
++
++static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ if (priv->flag_first) {
++ priv->flag_first = 0;
++ mdelay(200);
++ }
++
++ sf_pdm_enable(priv->pdm_map);
++ return 0;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ sf_pdm_disable(priv->pdm_map);
++ return 0;
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int sf_pdm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
++ unsigned int sample_rate;
++ unsigned int data_width;
++ int ret;
++ const int pdm_mul = 128;
++
++ sample_rate = params_rate(params);
++ switch (sample_rate) {
++ case 8000:
++ case 11025:
++ case 16000:
++ break;
++ default:
++ dev_err(priv->dev, "can't support sample rate:%d\n", sample_rate);
++ return -EINVAL;
++ }
++
++ data_width = params_width(params);
++ switch (data_width) {
++ case 16:
++ case 32:
++ break;
++ default:
++ dev_err(priv->dev, "can't support bit width %d\n", data_width);
++ return -EINVAL;
++ }
++
++ /* set pdm_mclk, PDM MCLK = 128 * LRCLK */
++ ret = clk_set_rate(priv->clk_pdm_mclk, pdm_mul * sample_rate);
++ if (ret) {
++ dev_err(priv->dev, "Can't set pdm_mclk: %d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops sf_pdm_dai_ops = {
++ .trigger = sf_pdm_trigger,
++ .hw_params = sf_pdm_hw_params,
++};
++
++static void sf_pdm_module_init(struct sf_pdm *priv)
++{
++ /* Reset */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_SW_RST_MASK, 0x00);
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_SW_RST_MASK, PDM_SW_RST_RELEASE);
++
++ /* Make sure the device is initially disabled */
++ sf_pdm_disable(priv->pdm_map);
++
++ /* MUTE */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MUTE);
++
++ /* UNMUTE */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MAX);
++
++ /* enable high pass filter */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_HPF_EN, PDM_DMIC_HPF_EN);
++
++ /* PDM work as slave mode */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_I2S_SLAVE, PDM_DMIC_I2S_SLAVE);
++
++ /* disable fast mode */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_FASTMODE_MASK, 0);
++
++ /* dmic msb shift 0 */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_MSB_MASK, 0);
++
++ /* scale: 0x8 */
++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++ DMIC_SCALE_MASK, DMIC_SCALE_DEF_VAL);
++
++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++ DMIC_DCOFF1_MASK, DMIC_DCOFF1_VAL);
++
++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++ DMIC_DCOFF3_MASK, DMIC_DCOFF3_VAL);
++
++ /* scale: 0x3f */
++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++ DMIC_SCALE_MASK, DMIC_SCALE_MASK);
++
++ /* dmic msb shift 2 */
++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++ PDM_DMIC_MSB_MASK, PDM_MSB_SHIFT_4);
++}
++
++#define SF_PDM_RATES (SNDRV_PCM_RATE_8000 | \
++ SNDRV_PCM_RATE_11025 | \
++ SNDRV_PCM_RATE_16000)
++
++#define SF_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
++ SNDRV_PCM_FMTBIT_S32_LE)
++
++static struct snd_soc_dai_driver sf_pdm_dai_drv = {
++ .name = "PDM",
++ .id = 0,
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = SF_PDM_RATES,
++ .formats = SF_PDM_FORMATS,
++ },
++ .ops = &sf_pdm_dai_ops,
++ .symmetric_rate = 1,
++};
++
++static int sf_pdm_component_probe(struct snd_soc_component *component)
++{
++ struct sf_pdm *priv = snd_soc_component_get_drvdata(component);
++
++ snd_soc_component_init_regmap(component, priv->pdm_map);
++ snd_soc_add_component_controls(component, sf_pdm_snd_controls,
++ ARRAY_SIZE(sf_pdm_snd_controls));
++
++ return 0;
++}
++
++static int sf_pdm_clock_enable(struct sf_pdm *priv)
++{
++ int ret;
++
++ ret = clk_prepare_enable(priv->clk_pdm_mclk);
++ if (ret) {
++ dev_err(priv->dev, "failed to prepare enable clk_pdm_mclk\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(priv->clk_pdm_apb);
++ if (ret) {
++ dev_err(priv->dev, "failed to prepare enable clk_pdm_apb\n");
++ goto disable_pdm_mclk;
++ }
++
++ ret = reset_control_deassert(priv->rst_pdm_dmic);
++ if (ret) {
++ dev_err(priv->dev, "failed to deassert pdm_dmic\n");
++ goto disable_pdm_apb;
++ }
++
++ ret = reset_control_deassert(priv->rst_pdm_apb);
++ if (ret) {
++ dev_err(priv->dev, "failed to deassert pdm_apb\n");
++ goto disable_pdm_apb;
++ }
++
++ ret = clk_set_parent(priv->clk_mclk, priv->clk_mclk_ext);
++ if (ret) {
++ dev_err(priv->dev, "failed to set parent clk_mclk ret=%d\n", ret);
++ goto disable_pdm_apb;
++ }
++
++ return 0;
++
++disable_pdm_apb:
++ clk_disable_unprepare(priv->clk_pdm_apb);
++disable_pdm_mclk:
++ clk_disable_unprepare(priv->clk_pdm_mclk);
++
++ return ret;
++}
++
++#ifdef CONFIG_PM
++static int sf_pdm_runtime_suspend(struct device *dev)
++{
++ struct sf_pdm *priv = dev_get_drvdata(dev);
++
++ clk_disable_unprepare(priv->clk_pdm_apb);
++ clk_disable_unprepare(priv->clk_pdm_mclk);
++
++ return 0;
++}
++
++static int sf_pdm_runtime_resume(struct device *dev)
++{
++ struct sf_pdm *priv = dev_get_drvdata(dev);
++ int ret;
++
++ ret = sf_pdm_clock_enable(priv);
++ if (!ret)
++ sf_pdm_module_init(priv);
++
++ return ret;
++}
++#endif
++
++#ifdef CONFIG_PM_SLEEP
++static int sf_pdm_suspend(struct snd_soc_component *component)
++{
++ return pm_runtime_force_suspend(component->dev);
++}
++
++static int sf_pdm_resume(struct snd_soc_component *component)
++{
++ return pm_runtime_force_resume(component->dev);
++}
++
++#else
++#define sf_pdm_suspend NULL
++#define sf_pdm_resume NULL
++#endif
++
++static const struct snd_soc_component_driver sf_pdm_component_drv = {
++ .name = "jh7110-pdm",
++ .probe = sf_pdm_component_probe,
++ .suspend = sf_pdm_suspend,
++ .resume = sf_pdm_resume,
++};
++
++static const struct regmap_config sf_pdm_regmap_cfg = {
++ .reg_bits = 32,
++ .val_bits = 32,
++ .reg_stride = 4,
++ .max_register = 0x20,
++};
++
++static int sf_pdm_clock_get(struct platform_device *pdev, struct sf_pdm *priv)
++{
++ int ret;
++
++ static struct clk_bulk_data clks[] = {
++ { .id = "pdm_mclk" },
++ { .id = "pdm_apb" },
++ { .id = "clk_mclk" },
++ { .id = "mclk_ext" },
++ };
++
++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to get pdm clocks\n");
++ goto exit;
++ }
++
++ priv->clk_pdm_mclk = clks[0].clk;
++ priv->clk_pdm_apb = clks[1].clk;
++ priv->clk_mclk = clks[2].clk;
++ priv->clk_mclk_ext = clks[3].clk;
++
++ priv->rst_pdm_dmic = devm_reset_control_get_exclusive(&pdev->dev, "pdm_dmic");
++ if (IS_ERR(priv->rst_pdm_dmic)) {
++ dev_err(&pdev->dev, "failed to get pdm_dmic reset control\n");
++ ret = PTR_ERR(priv->rst_pdm_dmic);
++ goto exit;
++ }
++
++ priv->rst_pdm_apb = devm_reset_control_get_exclusive(&pdev->dev, "pdm_apb");
++ if (IS_ERR(priv->rst_pdm_apb)) {
++ dev_err(&pdev->dev, "failed to get pdm_apb reset control\n");
++ ret = PTR_ERR(priv->rst_pdm_apb);
++ goto exit;
++ }
++
++ /*
++ * pdm clock must always be enabled as hardware issue that
++ * no data in the first 4 seconds of the first recording
++ */
++ ret = sf_pdm_clock_enable(priv);
++
++exit:
++ return ret;
++}
++
++static int sf_pdm_probe(struct platform_device *pdev)
++{
++ struct sf_pdm *priv;
++ struct resource *res;
++ void __iomem *regs;
++ int ret;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++ platform_set_drvdata(pdev, priv);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdm");
++ regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ priv->pdm_map = devm_regmap_init_mmio(&pdev->dev, regs, &sf_pdm_regmap_cfg);
++ if (IS_ERR(priv->pdm_map)) {
++ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
++ PTR_ERR(priv->pdm_map));
++ return PTR_ERR(priv->pdm_map);
++ }
++
++ priv->dev = &pdev->dev;
++ priv->flag_first = 1;
++
++ ret = sf_pdm_clock_get(pdev, priv);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to enable audio-pdm clock\n");
++ return ret;
++ }
++
++ dev_set_drvdata(&pdev->dev, priv);
++
++ ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
++ &sf_pdm_dai_drv, 1);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to register pdm dai\n");
++ return ret;
++ }
++ pm_runtime_enable(&pdev->dev);
++
++ return 0;
++}
++
++static int sf_pdm_dev_remove(struct platform_device *pdev)
++{
++ pm_runtime_disable(&pdev->dev);
++ return 0;
++}
++
++static const struct of_device_id sf_pdm_of_match[] = {
++ {.compatible = "starfive,jh7110-pdm",},
++ {}
++};
++MODULE_DEVICE_TABLE(of, sf_pdm_of_match);
++
++static const struct dev_pm_ops sf_pdm_pm_ops = {
++ SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend,
++ sf_pdm_runtime_resume, NULL)
++};
++
++static struct platform_driver sf_pdm_driver = {
++ .driver = {
++ .name = "jh7110-pdm",
++ .of_match_table = sf_pdm_of_match,
++ .pm = &sf_pdm_pm_ops,
++ },
++ .probe = sf_pdm_probe,
++ .remove = sf_pdm_dev_remove,
++};
++module_platform_driver(sf_pdm_driver);
++
++MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
++MODULE_DESCRIPTION("Starfive PDM Controller Driver");
++MODULE_LICENSE("GPL v2");
--- /dev/null
+From 59cbdfeee0fc1ad382a0bc8f7fa897a9f5d03df0 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Fri, 9 Jun 2023 16:54:36 +0800
+Subject: [PATCH 082/116] dt-binding: input: Add tink_ft5406
+
+Add tink_ft5406.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../bindings/input/tinker_ft5406.yaml | 39 +++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/input/tinker_ft5406.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/input/tinker_ft5406.yaml
+@@ -0,0 +1,39 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Tinker FT5406 touchscreen controller Bindings
++
++maintainers:
++ - Changhuang Liang <changhuang.liang@starfivetech.com>
++
++allOf:
++ - $ref: touchscreen.yaml#
++
++properties:
++ compatible:
++ const: tinker_ft5406
++
++ reg:
++ const: 0x38
++
++additionalProperties: false
++
++required:
++ - compatible
++ - reg
++
++examples:
++ - |
++ i2c {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ tinker_ft5406@38 {
++ compatible = "tinker_ft5406";
++ reg = <0x38>;
++ };
++ };
++
++...
--- /dev/null
+From 4800b6e0f2190d991cd4e5352167a9422841f195 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 21 Dec 2022 16:20:04 +0800
+Subject: [PATCH 083/116] input: touchscreen: Add tinker_ft5406 driver support
+
+Add tinker_ft5406 driver support
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/input/touchscreen/Kconfig | 6 +
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/tinker_ft5406.c | 406 ++++++++++++++++++++++
+ 3 files changed, 413 insertions(+)
+ create mode 100644 drivers/input/touchscreen/tinker_ft5406.c
+
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -1399,4 +1399,10 @@ config TOUCHSCREEN_HIMAX_HX83112B
+ To compile this driver as a module, choose M here: the
+ module will be called himax_hx83112b.
+
++config TOUCHSCREEN_TINKER_FT5406
++ tristate "tinker ft5406"
++ depends on I2C
++ help
++ Control ft5406 touch ic.
++
+ endif
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -118,3 +118,4 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5
+ obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o
+ obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
+ obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o
++obj-$(CONFIG_TOUCHSCREEN_TINKER_FT5406) += tinker_ft5406.o
+--- /dev/null
++++ b/drivers/input/touchscreen/tinker_ft5406.c
+@@ -0,0 +1,406 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ *
++ * TINKER BOARD FT5406 touch driver.
++ *
++ * Copyright (c) 2016 ASUSTek Computer Inc.
++ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/input.h>
++#include <linux/input/mt.h>
++#include <linux/module.h>
++#include <linux/workqueue.h>
++
++#define RETRY_COUNT 10
++#define FT_ONE_TCH_LEN 6
++
++#define FT_REG_FW_VER 0xA6
++#define FT_REG_FW_MIN_VER 0xB2
++#define FT_REG_FW_SUB_MIN_VER 0xB3
++
++#define VALID_TD_STATUS_VAL 10
++#define MAX_TOUCH_POINTS 5
++
++#define FT_PRESS 0x7F
++#define FT_MAX_ID 0x0F
++
++#define FT_TOUCH_X_H 0
++#define FT_TOUCH_X_L 1
++#define FT_TOUCH_Y_H 2
++#define FT_TOUCH_Y_L 3
++#define FT_TOUCH_EVENT 0
++#define FT_TOUCH_ID 2
++
++#define FT_TOUCH_X_H_REG 3
++#define FT_TOUCH_X_L_REG 4
++#define FT_TOUCH_Y_H_REG 5
++#define FT_TOUCH_Y_L_REG 6
++#define FT_TD_STATUS_REG 2
++#define FT_TOUCH_EVENT_REG 3
++#define FT_TOUCH_ID_REG 5
++
++#define FT_TOUCH_DOWN 0
++#define FT_TOUCH_CONTACT 2
++
++struct ts_event {
++ u16 au16_x[MAX_TOUCH_POINTS]; /*x coordinate */
++ u16 au16_y[MAX_TOUCH_POINTS]; /*y coordinate */
++ u8 au8_touch_event[MAX_TOUCH_POINTS]; /*touch event: 0:down; 1:up; 2:contact */
++ u8 au8_finger_id[MAX_TOUCH_POINTS]; /*touch ID */
++ u16 pressure;
++ u8 touch_point;
++ u8 point_num;
++};
++
++struct tinker_ft5406_data {
++ struct device *dev;
++ struct i2c_client *client;
++ struct input_dev *input_dev;
++ struct ts_event event;
++ struct work_struct ft5406_work;
++
++ int screen_width;
++ int screen_height;
++ int xy_reverse;
++ int known_ids;
++ int retry_count;
++ bool finish_work;
++};
++
++struct tinker_ft5406_data *g_ts_data;
++
++static int fts_i2c_read(struct i2c_client *client, char *writebuf,
++ int writelen, char *readbuf, int readlen)
++{
++ int ret;
++
++ if (writelen > 0) {
++ struct i2c_msg msgs[] = {
++ {
++ .addr = client->addr,
++ .flags = 0,
++ .len = writelen,
++ .buf = writebuf,
++ },
++ {
++ .addr = client->addr,
++ .flags = I2C_M_RD,
++ .len = readlen,
++ .buf = readbuf,
++ },
++ };
++ ret = i2c_transfer(client->adapter, msgs, 2);
++ if (ret < 0)
++ dev_err(&client->dev, "i2c read error, %d\n", ret);
++ } else {
++ struct i2c_msg msgs[] = {
++ {
++ .addr = client->addr,
++ .flags = I2C_M_RD,
++ .len = readlen,
++ .buf = readbuf,
++ },
++ };
++ ret = i2c_transfer(client->adapter, msgs, 1);
++ if (ret < 0)
++ dev_err(&client->dev, "i2c read error, %d\n", ret);
++ }
++
++ return ret;
++}
++
++static int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val)
++{
++ return fts_i2c_read(client, &addr, 1, val, 1);
++}
++
++static int fts_check_fw_ver(struct i2c_client *client)
++{
++ u8 reg_addr, fw_ver[3];
++ int ret;
++
++ reg_addr = FT_REG_FW_VER;
++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[0], 1);
++ if (ret < 0)
++ goto error;
++
++ reg_addr = FT_REG_FW_MIN_VER;
++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[1], 1);
++ if (ret < 0)
++ goto error;
++
++ reg_addr = FT_REG_FW_SUB_MIN_VER;
++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[2], 1);
++ if (ret < 0)
++ goto error;
++
++ dev_info(&client->dev, "Firmware version = %d.%d.%d\n",
++ fw_ver[0], fw_ver[1], fw_ver[2]);
++ return 0;
++
++error:
++ return ret;
++}
++
++static int fts_read_td_status(struct tinker_ft5406_data *ts_data)
++{
++ u8 td_status;
++ int ret = -1;
++
++ ret = fts_read_reg(ts_data->client, FT_TD_STATUS_REG, &td_status);
++ if (ret < 0) {
++ dev_err(&ts_data->client->dev,
++ "Get reg td_status failed, %d\n", ret);
++ return ret;
++ }
++ return (int)td_status;
++}
++
++static int fts_read_touchdata(struct tinker_ft5406_data *ts_data)
++{
++ struct ts_event *event = &ts_data->event;
++ int ret = -1, i;
++ u8 buf[FT_ONE_TCH_LEN-2] = { 0 };
++ u8 reg_addr, pointid = FT_MAX_ID;
++
++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
++ reg_addr = FT_TOUCH_X_H_REG + (i * FT_ONE_TCH_LEN);
++ ret = fts_i2c_read(ts_data->client, ®_addr, 1, buf, FT_ONE_TCH_LEN-2);
++ if (ret < 0) {
++ dev_err(&ts_data->client->dev, "Read touchdata failed.\n");
++ return ret;
++ }
++
++ pointid = (buf[FT_TOUCH_ID]) >> 4;
++ if (pointid >= MAX_TOUCH_POINTS)
++ break;
++ event->au8_finger_id[i] = pointid;
++ event->au16_x[i] = (s16) (buf[FT_TOUCH_X_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_X_L];
++ event->au16_y[i] = (s16) (buf[FT_TOUCH_Y_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_Y_L];
++ event->au8_touch_event[i] = buf[FT_TOUCH_EVENT] >> 6;
++
++ if (ts_data->xy_reverse) {
++ event->au16_x[i] = ts_data->screen_width - event->au16_x[i] - 1;
++ event->au16_y[i] = ts_data->screen_height - event->au16_y[i] - 1;
++ }
++ }
++ event->pressure = FT_PRESS;
++
++ return 0;
++}
++
++static void fts_report_value(struct tinker_ft5406_data *ts_data)
++{
++ struct ts_event *event = &ts_data->event;
++ int i, modified_ids = 0, released_ids;
++
++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
++ if (event->au8_touch_event[i] == FT_TOUCH_DOWN ||
++ event->au8_touch_event[i] == FT_TOUCH_CONTACT) {
++ modified_ids |= 1 << event->au8_finger_id[i];
++ input_mt_slot(ts_data->input_dev, event->au8_finger_id[i]);
++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER,
++ true);
++ input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR,
++ event->pressure);
++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X,
++ event->au16_x[i]);
++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y,
++ event->au16_y[i]);
++
++ if (!((1 << event->au8_finger_id[i]) & ts_data->known_ids))
++ dev_dbg(&ts_data->client->dev, "Touch id-%d: x = %d, y = %d\n",
++ event->au8_finger_id[i],
++ event->au16_x[i],
++ event->au16_y[i]);
++ }
++ }
++
++ released_ids = ts_data->known_ids & ~modified_ids;
++ for (i = 0; released_ids && i < MAX_TOUCH_POINTS; i++) {
++ if (released_ids & (1<<i)) {
++ dev_dbg(&ts_data->client->dev, "Release id-%d, known = %x modified = %x\n",
++ i, ts_data->known_ids, modified_ids);
++ input_mt_slot(ts_data->input_dev, i);
++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false);
++ modified_ids &= ~(1 << i);
++ }
++ }
++ ts_data->known_ids = modified_ids;
++ input_mt_report_pointer_emulation(ts_data->input_dev, true);
++ input_sync(ts_data->input_dev);
++}
++
++static void fts_retry_clear(struct tinker_ft5406_data *ts_data)
++{
++ if (ts_data->retry_count != 0)
++ ts_data->retry_count = 0;
++}
++
++static int fts_retry_wait(struct tinker_ft5406_data *ts_data)
++{
++ if (ts_data->retry_count < RETRY_COUNT) {
++ dev_info(&ts_data->client->dev,
++ "Wait and retry, count = %d\n", ts_data->retry_count);
++ ts_data->retry_count++;
++ msleep(1000);
++ return 1;
++ }
++ dev_err(&ts_data->client->dev, "Attach retry count\n");
++ return 0;
++}
++
++static void tinker_ft5406_work(struct work_struct *work)
++{
++ struct ts_event *event = &g_ts_data->event;
++ int ret = 0, td_status;
++
++ /* polling 60fps */
++ while (!g_ts_data->finish_work) {
++ td_status = fts_read_td_status(g_ts_data);
++ if (td_status < 0) {
++ ret = fts_retry_wait(g_ts_data);
++ if (ret == 0) {
++ dev_err(&g_ts_data->client->dev, "Stop touch polling\n");
++ break;
++ }
++ } else if (td_status < VALID_TD_STATUS_VAL + 1 &&
++ (td_status > 0 || g_ts_data->known_ids != 0)) {
++ fts_retry_clear(g_ts_data);
++ memset(event, -1, sizeof(struct ts_event));
++ event->touch_point = td_status;
++ ret = fts_read_touchdata(g_ts_data);
++ if (ret == 0)
++ fts_report_value(g_ts_data);
++ }
++ msleep_interruptible(17);
++ }
++}
++
++static int tinker_ft5406_open(struct input_dev *dev)
++{
++ schedule_work(&g_ts_data->ft5406_work);
++ return 0;
++}
++
++static void tinker_ft5406_close(struct input_dev *dev)
++{
++ g_ts_data->finish_work = true;
++ cancel_work_sync(&g_ts_data->ft5406_work);
++ g_ts_data->finish_work = false;
++}
++
++static int tinker_ft5406_probe(struct i2c_client *client)
++{
++ struct input_dev *input_dev;
++ int ret = 0;
++
++ dev_info(&client->dev, "Address = 0x%x\n", client->addr);
++
++ g_ts_data = kzalloc(sizeof(struct tinker_ft5406_data), GFP_KERNEL);
++ if (g_ts_data == NULL) {
++ dev_err(&client->dev, "No memory for device\n");
++ return -ENOMEM;
++ }
++
++ g_ts_data->client = client;
++ i2c_set_clientdata(client, g_ts_data);
++
++ g_ts_data->screen_width = 800;
++ g_ts_data->screen_height = 480;
++ g_ts_data->xy_reverse = 1;
++
++ dev_info(&client->dev, "width = %d, height = %d, reverse = %d\n",
++ g_ts_data->screen_width, g_ts_data->screen_height, g_ts_data->xy_reverse);
++
++ ret = fts_check_fw_ver(g_ts_data->client);
++ if (ret) {
++ dev_err(&client->dev, "Checking touch ic failed\n");
++ goto check_fw_err;
++ }
++
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ dev_err(&client->dev, "Failed to allocate input device\n");
++ goto input_allocate_failed;
++ }
++ input_dev->name = "fts_ts";
++ input_dev->id.bustype = BUS_I2C;
++ input_dev->dev.parent = &g_ts_data->client->dev;
++ input_dev->open = tinker_ft5406_open;
++ input_dev->close = tinker_ft5406_close;
++
++ g_ts_data->input_dev = input_dev;
++ input_set_drvdata(input_dev, g_ts_data);
++
++ __set_bit(EV_SYN, input_dev->evbit);
++ __set_bit(EV_KEY, input_dev->evbit);
++ __set_bit(EV_ABS, input_dev->evbit);
++ __set_bit(BTN_TOUCH, input_dev->keybit);
++
++ input_mt_init_slots(input_dev, MAX_TOUCH_POINTS, 0);
++ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, g_ts_data->screen_width, 0, 0);
++ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, g_ts_data->screen_height, 0, 0);
++
++ ret = input_register_device(input_dev);
++ if (ret) {
++ dev_err(&client->dev, "Input device registration failed\n");
++ goto input_register_failed;
++ }
++
++ INIT_WORK(&g_ts_data->ft5406_work, tinker_ft5406_work);
++
++ return 0;
++
++input_register_failed:
++ input_free_device(input_dev);
++input_allocate_failed:
++check_fw_err:
++ kfree(g_ts_data);
++ g_ts_data = NULL;
++ return ret;
++}
++
++static void tinker_ft5406_remove(struct i2c_client *client)
++{
++ cancel_work_sync(&g_ts_data->ft5406_work);
++ if (g_ts_data->input_dev) {
++ input_unregister_device(g_ts_data->input_dev);
++ input_free_device(g_ts_data->input_dev);
++ }
++ kfree(g_ts_data);
++ g_ts_data = NULL;
++}
++
++static const struct i2c_device_id tinker_ft5406_id[] = {
++ {"tinker_ft5406", 0},
++ {},
++};
++
++static struct i2c_driver tinker_ft5406_driver = {
++ .driver = {
++ .name = "tinker_ft5406",
++ },
++ .probe = tinker_ft5406_probe,
++ .remove = tinker_ft5406_remove,
++ .id_table = tinker_ft5406_id,
++};
++module_i2c_driver(tinker_ft5406_driver);
++
++MODULE_DESCRIPTION("TINKER BOARD FT5406 Touch driver");
++MODULE_LICENSE("GPL v2");
--- /dev/null
+From b477a1a53553336edcfeb83be1b35817928daed8 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 5 Jun 2023 14:46:16 +0800
+Subject: [PATCH 084/116] dt-binding: media: Add JH7110 Camera Subsystem.
+
+Add the bindings documentation for Starfive JH7110 Camera Subsystem
+which is used for handing image sensor data.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Jack Zhu <jack.zhu@starfivetech.com>
+---
+ .../bindings/media/starfive,jh7110-camss.yaml | 228 ++++++++++++++++++
+ 1 file changed, 228 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
+@@ -0,0 +1,228 @@
++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Starfive SoC CAMSS ISP
++
++maintainers:
++ - Jack Zhu <jack.zhu@starfivetech.com>
++ - Changhuang Liang <changhuang.liang@starfivetech.com>
++
++description:
++ The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It
++ consists of a VIN controller (Video In Controller, a top-level control unit)
++ and an ISP.
++
++properties:
++ compatible:
++ const: starfive,jh7110-vin
++
++ reg:
++ maxItems: 8
++
++ reg-names:
++ items:
++ - const: csi2rx
++ - const: vclk
++ - const: vrst
++ - const: sctrl
++ - const: isp
++ - const: trst
++ - const: pmu
++ - const: syscrg
++
++ clocks:
++ maxItems: 16
++
++ clock-names:
++ items:
++ - const: clk_apb_func
++ - const: clk_pclk
++ - const: clk_sys_clk
++ - const: clk_wrapper_clk_c
++ - const: clk_dvp_inv
++ - const: clk_axiwr
++ - const: clk_mipi_rx0_pxl
++ - const: clk_pixel_clk_if0
++ - const: clk_pixel_clk_if1
++ - const: clk_pixel_clk_if2
++ - const: clk_pixel_clk_if3
++ - const: clk_m31dphy_cfgclk_in
++ - const: clk_m31dphy_refclk_in
++ - const: clk_m31dphy_txclkesc_lan0
++ - const: clk_ispcore_2x
++ - const: clk_isp_axi
++
++ resets:
++ maxItems: 14
++
++ reset-names:
++ items:
++ - const: rst_wrapper_p
++ - const: rst_wrapper_c
++ - const: rst_pclk
++ - const: rst_sys_clk
++ - const: rst_axird
++ - const: rst_axiwr
++ - const: rst_pixel_clk_if0
++ - const: rst_pixel_clk_if1
++ - const: rst_pixel_clk_if2
++ - const: rst_pixel_clk_if3
++ - const: rst_m31dphy_hw
++ - const: rst_m31dphy_b09_always_on
++ - const: rst_isp_top_n
++ - const: rst_isp_top_axi
++
++ power-domains:
++ items:
++ - description: JH7110 ISP Power Domain Switch Controller.
++
++ interrupts:
++ maxItems: 5
++
++ ports:
++ $ref: /schemas/graph.yaml#/properties/ports
++
++ properties:
++ port@0:
++ $ref: /schemas/graph.yaml#/$defs/port-base
++ unevaluatedProperties: false
++ description: Input port for receiving DVP data.
++
++ properties:
++ endpoint:
++ $ref: video-interfaces.yaml#
++ unevaluatedProperties: false
++
++ properties:
++ bus-type:
++ enum: [5, 6]
++
++ bus-width:
++ enum: [8, 10, 12]
++
++ data-shift:
++ enum: [0, 2]
++ default: 0
++
++ hsync-active:
++ enum: [0, 1]
++ default: 1
++
++ vsync-active:
++ enum: [0, 1]
++ default: 1
++
++ required:
++ - bus-type
++ - bus-width
++
++ port@1:
++ $ref: /schemas/graph.yaml#/properties/port
++ description: Input port for receiving CSI data.
++
++ required:
++ - port@0
++ - port@1
++
++required:
++ - compatible
++ - reg
++ - reg-names
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
++ - power-domains
++ - interrupts
++ - ports
++
++additionalProperties: false
++
++examples:
++ - |
++ vin_sysctl: vin_sysctl@19800000 {
++ compatible = "starfive,jh7110-vin";
++ reg = <0x0 0x19800000 0x0 0x10000>,
++ <0x0 0x19810000 0x0 0x10000>,
++ <0x0 0x19820000 0x0 0x10000>,
++ <0x0 0x19840000 0x0 0x10000>,
++ <0x0 0x19870000 0x0 0x30000>,
++ <0x0 0x11840000 0x0 0x10000>,
++ <0x0 0x17030000 0x0 0x10000>,
++ <0x0 0x13020000 0x0 0x10000>;
++ reg-names = "csi2rx", "vclk", "vrst", "sctrl",
++ "isp", "trst", "pmu", "syscrg";
++ clocks = <&clkisp JH7110_DOM4_APB_FUNC>,
++ <&clkisp JH7110_U0_VIN_PCLK>,
++ <&clkisp JH7110_U0_VIN_SYS_CLK>,
++ <&clkisp JH7110_U0_ISPV2_TOP_WRAPPER_CLK_C>,
++ <&clkisp JH7110_DVP_INV>,
++ <&clkisp JH7110_U0_VIN_CLK_P_AXIWR>,
++ <&clkisp JH7110_MIPI_RX0_PXL>,
++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF0>,
++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF1>,
++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF2>,
++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF3>,
++ <&clkisp JH7110_U0_M31DPHY_CFGCLK_IN>,
++ <&clkisp JH7110_U0_M31DPHY_REFCLK_IN>,
++ <&clkisp JH7110_U0_M31DPHY_TXCLKESC_LAN0>,
++ <&clkgen JH7110_ISP_TOP_CLK_ISPCORE_2X>,
++ <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>;
++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk",
++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr",
++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0",
++ "clk_pixel_clk_if1", "clk_pixel_clk_if2",
++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in",
++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0",
++ "clk_ispcore_2x", "clk_isp_axi";
++ resets = <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_P>,
++ <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_C>,
++ <&rstgen RSTN_U0_VIN_N_PCLK>,
++ <&rstgen RSTN_U0_VIN_N_SYS_CLK>,
++ <&rstgen RSTN_U0_VIN_P_AXIRD>,
++ <&rstgen RSTN_U0_VIN_P_AXIWR>,
++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF0>,
++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF1>,
++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF2>,
++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF3>,
++ <&rstgen RSTN_U0_M31DPHY_HW>,
++ <&rstgen RSTN_U0_M31DPHY_B09_ALWAYS_ON>,
++ <&rstgen RSTN_U0_DOM_ISP_TOP_N>,
++ <&rstgen RSTN_U0_DOM_ISP_TOP_AXI>;
++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk",
++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0",
++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3",
++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on",
++ "rst_isp_top_n", "rst_isp_top_axi";
++ starfive,aon-syscon = <&aon_syscon 0x00>;
++ power-domains = <&pwrc JH7110_PD_ISP>;
++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */
++ interrupts = <92>, <87>, <88>, <89>, <90>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ port@0 {
++ reg = <0>;
++ vin_from_sc2235: endpoint {
++ remote-endpoint = <&sc2235_to_vin>;
++ bus-type = <5>;
++ bus-width = <8>;
++ data-shift = <2>;
++ hsync-active = <1>;
++ vsync-active = <0>;
++ pclk-sample = <1>;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ vin_from_csi2rx: endpoint {
++ remote-endpoint = <&csi2rx_to_vin>;
++ };
++ };
++ };
++ };
--- /dev/null
+From 908b10ebc95eb29caae8c4737b23a29af5c6298f Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 5 Jun 2023 13:54:16 +0800
+Subject: [PATCH 085/116] media: starfive: Add vin driver support
+
+Add vin driver support.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/platform/Kconfig | 1 +
+ drivers/media/platform/Makefile | 1 +
+ drivers/media/platform/starfive/Kconfig | 56 +
+ drivers/media/platform/starfive/Makefile | 24 +
+ .../platform/starfive/v4l2_driver/Readme.txt | 11 +
+ .../starfive/v4l2_driver/imx219_mipi.c | 1583 ++++++++
+ .../starfive/v4l2_driver/ov13850_mipi.c | 1921 ++++++++++
+ .../starfive/v4l2_driver/ov4689_mipi.c | 2975 +++++++++++++++
+ .../platform/starfive/v4l2_driver/ov5640.c | 3227 +++++++++++++++++
+ .../platform/starfive/v4l2_driver/sc2235.c | 1914 ++++++++++
+ .../starfive/v4l2_driver/stf_common.h | 185 +
+ .../platform/starfive/v4l2_driver/stf_csi.c | 465 +++
+ .../platform/starfive/v4l2_driver/stf_csi.h | 61 +
+ .../starfive/v4l2_driver/stf_csi_hw_ops.c | 310 ++
+ .../starfive/v4l2_driver/stf_csiphy.c | 357 ++
+ .../starfive/v4l2_driver/stf_csiphy.h | 188 +
+ .../starfive/v4l2_driver/stf_csiphy_hw_ops.c | 335 ++
+ .../starfive/v4l2_driver/stf_dmabuf.c | 123 +
+ .../starfive/v4l2_driver/stf_dmabuf.h | 12 +
+ .../platform/starfive/v4l2_driver/stf_dvp.c | 385 ++
+ .../platform/starfive/v4l2_driver/stf_dvp.h | 67 +
+ .../starfive/v4l2_driver/stf_dvp_hw_ops.c | 187 +
+ .../platform/starfive/v4l2_driver/stf_event.c | 36 +
+ .../platform/starfive/v4l2_driver/stf_isp.c | 1521 ++++++++
+ .../platform/starfive/v4l2_driver/stf_isp.h | 222 ++
+ .../starfive/v4l2_driver/stf_isp_hw_ops.c | 1550 ++++++++
+ .../starfive/v4l2_driver/stf_isp_ioctl.h | 133 +
+ .../platform/starfive/v4l2_driver/stf_video.c | 1552 ++++++++
+ .../platform/starfive/v4l2_driver/stf_video.h | 83 +
+ .../platform/starfive/v4l2_driver/stf_vin.c | 1515 ++++++++
+ .../platform/starfive/v4l2_driver/stf_vin.h | 182 +
+ .../starfive/v4l2_driver/stf_vin_hw_ops.c | 433 +++
+ .../platform/starfive/v4l2_driver/stfcamss.c | 1369 +++++++
+ .../platform/starfive/v4l2_driver/stfcamss.h | 117 +
+ include/uapi/linux/jh7110-isp.h | 253 ++
+ include/uapi/linux/v4l2-controls.h | 6 +
+ include/video/stf-vin.h | 443 +++
+ 37 files changed, 23803 insertions(+)
+ create mode 100644 drivers/media/platform/starfive/Kconfig
+ create mode 100644 drivers/media/platform/starfive/Makefile
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/Readme.txt
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov5640.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/sc2235.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_common.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_event.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.h
+ create mode 100644 include/uapi/linux/jh7110-isp.h
+ create mode 100644 include/video/stf-vin.h
+
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/K
+ source "drivers/media/platform/rockchip/Kconfig"
+ source "drivers/media/platform/samsung/Kconfig"
+ source "drivers/media/platform/st/Kconfig"
++source "drivers/media/platform/starfive/Kconfig"
+ source "drivers/media/platform/sunxi/Kconfig"
+ source "drivers/media/platform/ti/Kconfig"
+ source "drivers/media/platform/verisilicon/Kconfig"
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -23,6 +23,7 @@ obj-y += renesas/
+ obj-y += rockchip/
+ obj-y += samsung/
+ obj-y += st/
++obj-y += starfive/
+ obj-y += sunxi/
+ obj-y += ti/
+ obj-y += verisilicon/
+--- /dev/null
++++ b/drivers/media/platform/starfive/Kconfig
+@@ -0,0 +1,56 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++comment "Starfive media platform drivers"
++
++config VIN_SENSOR_OV5640
++ tristate "VIN SENSOR support OV5640"
++ depends on VIDEO_STF_VIN
++ select V4L2_FWNODE
++ default n
++ help
++ Say Y here if you want to have support for VIN sensor OV5640
++
++config VIN_SENSOR_SC2235
++ tristate "VIN SENSOR support SC2235"
++ depends on VIDEO_STF_VIN
++ select V4L2_FWNODE
++ default n
++ help
++ Say Y here if you want to have support for VIN sensor SC2235
++
++config VIN_SENSOR_OV4689
++ tristate "VIN SENSOR support OV4689"
++ depends on VIDEO_STF_VIN
++ select V4L2_FWNODE
++ default n
++
++ help
++ Say Y here if you want to have support for VIN sensor OV4689
++
++config VIN_SENSOR_OV13850
++ bool "VIN SENSOR support OV13850"
++ depends on VIDEO_STF_VIN
++ select V4L2_FWNODE
++ default n
++ help
++ Say Y here if you want to have support for VIN sensor OV13850
++
++config VIN_SENSOR_IMX219
++ tristate "VIN SENSOR support IMX219"
++ depends on VIDEO_STF_VIN
++ select V4L2_FWNODE
++ default n
++ help
++ Say Y here if you want to have support for VIN sensor IMX219
++
++config VIDEO_STF_VIN
++ tristate "starfive VIC video in support"
++ depends on V4L_PLATFORM_DRIVERS
++ depends on VIDEO_DEV
++ select MEDIA_CONTROLLER
++ select VIDEOBUF2_DMA_CONTIG
++ select VIDEO_V4L2_SUBDEV_API
++ select V4L2_FWNODE
++ help
++ To compile this driver as a module, choose M here: the module
++ will be called stf-vin.
+--- /dev/null
++++ b/drivers/media/platform/starfive/Makefile
+@@ -0,0 +1,24 @@
++# SPDX-License-Identifier: GPL-2.0
++
++obj-$(CONFIG_VIN_SENSOR_OV5640) += v4l2_driver/ov5640.o
++obj-$(CONFIG_VIN_SENSOR_SC2235) += v4l2_driver/sc2235.o
++obj-$(CONFIG_VIN_SENSOR_OV4689) += v4l2_driver/ov4689_mipi.o
++obj-$(CONFIG_VIN_SENSOR_OV13850) += v4l2_driver/ov13850_mipi.o
++obj-$(CONFIG_VIN_SENSOR_IMX219) += v4l2_driver/imx219_mipi.o
++
++starfivecamss-objs += v4l2_driver/stfcamss.o \
++ v4l2_driver/stf_event.o \
++ v4l2_driver/stf_dvp.o \
++ v4l2_driver/stf_csi.o \
++ v4l2_driver/stf_csiphy.o \
++ v4l2_driver/stf_isp.o \
++ v4l2_driver/stf_video.o \
++ v4l2_driver/stf_vin.o \
++ v4l2_driver/stf_vin_hw_ops.o \
++ v4l2_driver/stf_csi_hw_ops.o \
++ v4l2_driver/stf_csiphy_hw_ops.o \
++ v4l2_driver/stf_isp_hw_ops.o \
++ v4l2_driver/stf_dvp_hw_ops.o \
++ v4l2_driver/stf_dmabuf.o
++
++obj-$(CONFIG_VIDEO_STF_VIN) += starfivecamss.o \
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/Readme.txt
+@@ -0,0 +1,11 @@
++
++/dev/video0: Output the camera data directly.
++/dev/video1: Output the data of the camera converted by isp.
++
++ensure linux/arch/riscv/configs/starfive_jh7110_defconfig:
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIN_SENSOR_SC2235=y
++CONFIG_VIN_SENSOR_OV4689=y
++
++Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4
++as data lane.
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
+@@ -0,0 +1,1583 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX219 cameras.
++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
++ *
++ * Based on Sony imx258 camera driver
++ * Copyright (C) 2018 Intel Corporation
++ *
++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
++ * Copyright 2018 Qtechnology A/S
++ *
++ * Flip handling taken from the Sony IMX319 driver.
++ * Copyright (C) 2018 Intel Corporation
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <asm/unaligned.h>
++
++#define IMX219_REG_VALUE_08BIT 1
++#define IMX219_REG_VALUE_16BIT 2
++
++#define IMX219_REG_MODE_SELECT 0x0100
++#define IMX219_MODE_STANDBY 0x00
++#define IMX219_MODE_STREAMING 0x01
++
++/* Chip ID */
++#define IMX219_REG_CHIP_ID 0x0000
++#define IMX219_CHIP_ID 0x0219
++
++/* External clock frequency is 24.0M */
++#define IMX219_XCLK_FREQ 24000000
++
++/* Pixel rate is fixed at 182.4M for all the modes */
++#define IMX219_PIXEL_RATE 182400000
++
++#define IMX219_DEFAULT_LINK_FREQ 456000000
++
++/* V_TIMING internal */
++#define IMX219_REG_VTS 0x0160
++#define IMX219_VTS_15FPS 0x0dc6
++#define IMX219_VTS_30FPS_1080P 0x06e3
++#define IMX219_VTS_30FPS_BINNED 0x06e3
++#define IMX219_VTS_30FPS_1280x720 0x06e3
++#define IMX219_VTS_30FPS_640x480 0x06e3
++#define IMX219_VTS_MAX 0xffff
++
++#define IMX219_VBLANK_MIN 4
++
++/*Frame Length Line*/
++#define IMX219_FLL_MIN 0x08a6
++#define IMX219_FLL_MAX 0xffff
++#define IMX219_FLL_STEP 1
++#define IMX219_FLL_DEFAULT 0x0c98
++
++/* HBLANK control - read only */
++#define IMX219_PPL_DEFAULT 3448
++
++/* Exposure control */
++#define IMX219_REG_EXPOSURE 0x015a
++#define IMX219_EXPOSURE_MIN 4
++#define IMX219_EXPOSURE_STEP 1
++#define IMX219_EXPOSURE_DEFAULT 0x640
++#define IMX219_EXPOSURE_MAX 65535
++
++/* Analog gain control */
++#define IMX219_REG_ANALOG_GAIN 0x0157
++#define IMX219_ANA_GAIN_MIN 0
++#define IMX219_ANA_GAIN_MAX 232
++#define IMX219_ANA_GAIN_STEP 1
++#define IMX219_ANA_GAIN_DEFAULT 0xd0
++
++/* Digital gain control */
++#define IMX219_REG_DIGITAL_GAIN 0x0158
++#define IMX219_DGTL_GAIN_MIN 0x0100
++#define IMX219_DGTL_GAIN_MAX 0x0fff
++#define IMX219_DGTL_GAIN_DEFAULT 0x0100
++#define IMX219_DGTL_GAIN_STEP 1
++
++#define IMX219_REG_ORIENTATION 0x0172
++
++/* Test Pattern Control */
++#define IMX219_REG_TEST_PATTERN 0x0600
++#define IMX219_TEST_PATTERN_DISABLE 0
++#define IMX219_TEST_PATTERN_SOLID_COLOR 1
++#define IMX219_TEST_PATTERN_COLOR_BARS 2
++#define IMX219_TEST_PATTERN_GREY_COLOR 3
++#define IMX219_TEST_PATTERN_PN9 4
++
++/* Test pattern colour components */
++#define IMX219_REG_TESTP_RED 0x0602
++#define IMX219_REG_TESTP_GREENR 0x0604
++#define IMX219_REG_TESTP_BLUE 0x0606
++#define IMX219_REG_TESTP_GREENB 0x0608
++#define IMX219_TESTP_COLOUR_MIN 0
++#define IMX219_TESTP_COLOUR_MAX 0x03ff
++#define IMX219_TESTP_COLOUR_STEP 1
++#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX
++#define IMX219_TESTP_GREENR_DEFAULT 0
++#define IMX219_TESTP_BLUE_DEFAULT 0
++#define IMX219_TESTP_GREENB_DEFAULT 0
++
++/* IMX219 native and active pixel array size. */
++#define IMX219_NATIVE_WIDTH 3296U
++#define IMX219_NATIVE_HEIGHT 2480U
++#define IMX219_PIXEL_ARRAY_LEFT 8U
++#define IMX219_PIXEL_ARRAY_TOP 8U
++#define IMX219_PIXEL_ARRAY_WIDTH 3280U
++#define IMX219_PIXEL_ARRAY_HEIGHT 2464U
++
++struct imx219_reg {
++ u16 address;
++ u8 val;
++};
++
++struct imx219_reg_list {
++ unsigned int num_of_regs;
++ const struct imx219_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx219_mode {
++ /* Frame width */
++ unsigned int width;
++ /* Frame height */
++ unsigned int height;
++
++ unsigned int fps;
++
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
++ /* V-timing */
++ unsigned int vts_def;
++
++ /* Default register values */
++ struct imx219_reg_list reg_list;
++};
++
++/*
++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
++ * driver.
++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
++ */
++
++static const struct imx219_reg mode_1920_1080_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x05},
++ {0x30eb, 0x0c},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++ {0x0164, 0x02},
++ {0x0165, 0xa8},
++ {0x0166, 0x0a},
++ {0x0167, 0x27},
++ {0x0168, 0x02},
++ {0x0169, 0xb4},
++ {0x016a, 0x06},
++ {0x016b, 0xeb},
++ {0x016c, 0x07},
++ {0x016d, 0x80},
++ {0x016e, 0x04},
++ {0x016f, 0x38},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x00},
++ {0x0175, 0x00},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x07},
++ {0x0625, 0x80},
++ {0x0626, 0x04},
++ {0x0627, 0x38},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++};
++
++static const struct imx219_reg mode_1280_720_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x05},
++ {0x30eb, 0x0c},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++ {0x0164, 0x01},
++ {0x0165, 0x68},
++ {0x0166, 0x0b},
++ {0x0167, 0x67},
++ {0x0168, 0x02},
++ {0x0169, 0x00},
++ {0x016a, 0x07},
++ {0x016b, 0x9f},
++ {0x016c, 0x05},
++ {0x016d, 0x00},
++ {0x016e, 0x02},
++ {0x016f, 0xd0},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x01},
++ {0x0175, 0x01},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x06},
++ {0x0625, 0x68},
++ {0x0626, 0x04},
++ {0x0627, 0xd0},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++};
++
++static const struct imx219_reg mode_640_480_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x05},
++ {0x30eb, 0x0c},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++ {0x0164, 0x03},
++ {0x0165, 0xe8},
++ {0x0166, 0x08},
++ {0x0167, 0xe7},
++ {0x0168, 0x02},
++ {0x0169, 0xf0},
++ {0x016a, 0x06},
++ {0x016b, 0xaf},
++ {0x016c, 0x02},
++ {0x016d, 0x80},
++ {0x016e, 0x01},
++ {0x016f, 0xe0},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x03},
++ {0x0175, 0x03},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x06},
++ {0x0625, 0x68},
++ {0x0626, 0x04},
++ {0x0627, 0xd0},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++};
++
++static const struct imx219_reg raw8_framefmt_regs[] = {
++ {0x018c, 0x08},
++ {0x018d, 0x08},
++ {0x0309, 0x08},
++};
++
++static const struct imx219_reg raw10_framefmt_regs[] = {
++ {0x018c, 0x0a},
++ {0x018d, 0x0a},
++ {0x0309, 0x0a},
++};
++
++static const s64 imx219_link_freq_menu[] = {
++ IMX219_DEFAULT_LINK_FREQ,
++};
++
++static const char * const imx219_test_pattern_menu[] = {
++ "Disabled",
++ "Color Bars",
++ "Solid Color",
++ "Grey Color Bars",
++ "PN9"
++};
++
++static const int imx219_test_pattern_val[] = {
++ IMX219_TEST_PATTERN_DISABLE,
++ IMX219_TEST_PATTERN_COLOR_BARS,
++ IMX219_TEST_PATTERN_SOLID_COLOR,
++ IMX219_TEST_PATTERN_GREY_COLOR,
++ IMX219_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx219_supply_name[] = {
++ /* Supplies can be enabled in any order */
++ "VANA", /* Analog (2.8V) supply */
++ "VDIG", /* Digital Core (1.8V) supply */
++ "VDDL", /* IF (1.2V) supply */
++};
++
++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++
++ MEDIA_BUS_FMT_SRGGB8_1X8,
++ MEDIA_BUS_FMT_SGRBG8_1X8,
++ MEDIA_BUS_FMT_SGBRG8_1X8,
++ MEDIA_BUS_FMT_SBGGR8_1X8,
++};
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software stanby) must be not less than:
++ * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
++ * where
++ * t4 is fixed, and is max 200uS,
++ * t5 is fixed, and is 6000uS,
++ * t6 depends on the sensor external clock, and is max 32000 clock periods.
++ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
++ * So for any acceptable external clock t6 is always within the range of
++ * 1185 to 5333 uS, and is always less than t5.
++ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
++ * initialize the sensor over I2C, and then exit the software standby.
++ *
++ * This start-up time can be optimized a bit more, if we start the writes
++ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
++ * initialization over I2C may complete before (t4+t5) expires, and we must
++ * ensure that capture is not started before (t4+t5).
++ *
++ * This delay doesn't account for the power supply startup time. If needed,
++ * this should be taken care of via the regulator framework. E.g. in the
++ * case of DT for regulator-fixed one should define the startup-delay-us
++ * property.
++ */
++#define IMX219_XCLR_MIN_DELAY_US 6200
++#define IMX219_XCLR_DELAY_RANGE_US 1000
++
++/* Mode configs */
++static const struct imx219_mode supported_modes[] = {
++ {
++ /* 1080P 30fps cropped */
++ .width = 1920,
++ .height = 1080,
++ .fps = 30,
++ .crop = {
++ .left = 688,
++ .top = 700,
++ .width = 1920,
++ .height = 1080
++ },
++ .vts_def = IMX219_VTS_30FPS_1080P,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
++ .regs = mode_1920_1080_regs,
++ },
++ },
++ {
++ /* 1280x720 30fps mode */
++ .width = 1280,
++ .height = 720,
++ .fps = 30,
++ .crop = {
++ .left = 360,
++ .top = 512,
++ .width = 2560,
++ .height = 1440
++ },
++ .vts_def = IMX219_VTS_30FPS_1280x720,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_1280_720_regs),
++ .regs = mode_1280_720_regs,
++ },
++ },
++ {
++ /* 640x480 30fps mode */
++ .width = 640,
++ .height = 480,
++ .fps = 30,
++ .crop = {
++ .left = 1008,
++ .top = 760,
++ .width = 1280,
++ .height = 960
++ },
++ .vts_def = IMX219_VTS_30FPS_640x480,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
++ .regs = mode_640_480_regs,
++ },
++ },
++};
++
++struct imx219 {
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ //struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++
++ struct v4l2_mbus_framefmt fmt;
++
++ struct clk *xclk; /* system clock to IMX219 */
++ u32 xclk_freq;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ /* V4L2 Controls */
++ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *link_freq;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++
++ /* Current mode */
++ const struct imx219_mode *mode;
++ struct v4l2_fract frame_interval;
++
++ /*
++ * Mutex for serialized access:
++ * Protect sensor module set pad format and start/stop streaming safely.
++ */
++ struct mutex mutex;
++
++ /* Streaming on/off */
++ int streaming;
++};
++
++static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
++{
++ return container_of(_sd, struct imx219, sd);
++}
++
++/* Read registers up to 2 at a time */
++static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ struct i2c_msg msgs[2];
++ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++ u8 data_buf[4] = { 0, };
++ int ret;
++
++ if (len > 4)
++ return -EINVAL;
++
++ /* Write register address */
++ msgs[0].addr = client->addr;
++ msgs[0].flags = 0;
++ msgs[0].len = ARRAY_SIZE(addr_buf);
++ msgs[0].buf = addr_buf;
++
++ /* Read data from register */
++ msgs[1].addr = client->addr;
++ msgs[1].flags = I2C_M_RD;
++ msgs[1].len = len;
++ msgs[1].buf = &data_buf[4 - len];
++
++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++ if (ret != ARRAY_SIZE(msgs))
++ return -EIO;
++
++ *val = get_unaligned_be32(data_buf);
++
++ return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ u8 buf[6];
++
++ if (len > 4)
++ return -EINVAL;
++
++ put_unaligned_be16(reg, buf);
++ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++ if (i2c_master_send(client, buf, len + 2) != len + 2)
++ return -EIO;
++
++ return 0;
++}
++
++/* Write a list of registers */
++static int imx219_write_regs(struct imx219 *imx219,
++ const struct imx219_reg *regs, u32 len)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < len; i++) {
++ ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
++ if (ret) {
++ dev_err_ratelimited(&client->dev,
++ "Failed to write reg 0x%4.4x. error = %d\n",
++ regs[i].address, ret);
++
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx219_get_format_code(struct imx219 *imx219, u32 code)
++{
++ unsigned int i;
++
++ lockdep_assert_held(&imx219->mutex);
++
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == code)
++ break;
++
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
++ i = (i & ~3) | (imx219->vflip->val ? 2 : 0) |
++ (imx219->hflip->val ? 1 : 0);
++
++ return codes[i];
++}
++
++static void imx219_set_default_format(struct imx219 *imx219)
++{
++ struct v4l2_mbus_framefmt *fmt;
++
++ fmt = &imx219->fmt;
++ fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace, fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = supported_modes[0].width;
++ fmt->height = supported_modes[0].height;
++ fmt->field = V4L2_FIELD_NONE;
++}
++
++static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(sd, fh->state, 0);
++ struct v4l2_rect *try_crop;
++
++ mutex_lock(&imx219->mutex);
++
++ /* Initialize try_fmt */
++ try_fmt->width = supported_modes[0].width;
++ try_fmt->height = supported_modes[0].height;
++ try_fmt->code = imx219_get_format_code(imx219, MEDIA_BUS_FMT_SRGGB10_1X10);
++ try_fmt->field = V4L2_FIELD_NONE;
++
++ /* Initialize try_crop rectangle. */
++ try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0);
++ try_crop->top = IMX219_PIXEL_ARRAY_TOP;
++ try_crop->left = IMX219_PIXEL_ARRAY_LEFT;
++ try_crop->width = IMX219_PIXEL_ARRAY_WIDTH;
++ try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT;
++
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct imx219 *imx219 =
++ container_of(ctrl->handler, struct imx219, ctrl_handler);
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++
++ if (ctrl->id == V4L2_CID_VBLANK) {
++ int exposure_max, exposure_def;
++
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = imx219->mode->height + ctrl->val - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum,
++ exposure_max, imx219->exposure->step, exposure_def);
++ }
++
++ /*
++ * Applying V4L2 control value only happens
++ * when power is up for streaming
++ */
++ if (pm_runtime_get_if_in_use(&client->dev) == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
++ IMX219_REG_VALUE_08BIT, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_DIGITAL_GAIN:
++ ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
++ IMX219_REG_VALUE_16BIT, imx219_test_pattern_val[ctrl->val]);
++ break;
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
++ imx219->hflip->val | imx219->vflip->val << 1);
++ break;
++ case V4L2_CID_VBLANK:
++ ret = imx219_write_reg(imx219, IMX219_REG_VTS, IMX219_REG_VALUE_16BIT,
++ imx219->mode->height + ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_RED:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENR:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_BLUE:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENB:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_put(&client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
++ .s_ctrl = imx219_set_ctrl,
++};
++
++static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++
++ if (code->index >= (ARRAY_SIZE(codes) / 4))
++ return -EINVAL;
++
++ mutex_lock(&imx219->mutex);
++ code->code = imx219_get_format_code(imx219, codes[code->index * 4]);
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ u32 code;
++
++ if (fse->index >= ARRAY_SIZE(supported_modes))
++ return -EINVAL;
++
++ mutex_lock(&imx219->mutex);
++ code = imx219_get_format_code(imx219, fse->code);
++ mutex_unlock(&imx219->mutex);
++ if (fse->code != code)
++ return -EINVAL;
++
++ fse->min_width = supported_modes[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = supported_modes[fse->index].height;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int imx219_try_frame_interval(struct imx219 *imx219,
++ struct v4l2_fract *fi,
++ u32 w, u32 h)
++{
++ const struct imx219_mode *mode;
++
++ mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
++ width, height, w, h);
++ if (!mode || (mode->width != w || mode->height != h))
++ return -EINVAL;
++
++ fi->numerator = 1;
++ fi->denominator = mode->fps;
++
++ return mode->fps;
++}
++
++static int imx219_enum_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_interval_enum *fie)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ struct v4l2_fract tpf;
++ u32 code;
++ int ret;
++
++ if (fie->index >= ARRAY_SIZE(supported_modes))
++ return -EINVAL;
++
++ mutex_lock(&imx219->mutex);
++ code = imx219_get_format_code(imx219, fie->code);
++ if (fie->code != code) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ret = imx219_try_frame_interval(imx219, &tpf,
++ fie->width, fie->height);
++ if (ret < 0) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ mutex_unlock(&imx219->mutex);
++ fie->interval = tpf;
++
++ return 0;
++
++out:
++ mutex_unlock(&imx219->mutex);
++ return ret;
++}
++
++static int imx219_g_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++
++ mutex_lock(&imx219->mutex);
++ fi->interval = imx219->frame_interval;
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_s_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ const struct imx219_mode *mode = imx219->mode;
++ int frame_rate, ret = 0;
++
++ if (fi->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&imx219->mutex);
++
++ if (imx219->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ frame_rate = imx219_try_frame_interval(imx219, &fi->interval,
++ mode->width, mode->height);
++ if (frame_rate < 0) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ imx219->frame_interval = fi->interval;
++
++out:
++ mutex_unlock(&imx219->mutex);
++ return ret;
++}
++
++static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace, fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx219_update_pad_format(struct imx219 *imx219,
++ const struct imx219_mode *mode,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = mode->width;
++ fmt->format.height = mode->height;
++ fmt->format.field = V4L2_FIELD_NONE;
++ imx219_reset_colorspace(&fmt->format);
++}
++
++static int __imx219_get_pad_format(struct imx219 *imx219,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(&imx219->sd, state, fmt->pad);
++ /* update the code which could change due to vflip or hflip: */
++ try_fmt->code = imx219_get_format_code(imx219, try_fmt->code);
++ fmt->format = *try_fmt;
++ } else {
++ imx219_update_pad_format(imx219, imx219->mode, fmt);
++ fmt->format.code = imx219_get_format_code(imx219, imx219->fmt.code);
++ }
++
++ return 0;
++}
++
++static int imx219_get_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ int ret;
++
++ mutex_lock(&imx219->mutex);
++ ret = __imx219_get_pad_format(imx219, state, fmt);
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++}
++
++static int imx219_set_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ const struct imx219_mode *mode;
++ struct v4l2_mbus_framefmt *framefmt;
++ int exposure_max, exposure_def, hblank;
++ unsigned int i;
++
++ mutex_lock(&imx219->mutex);
++
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == fmt->format.code)
++ break;
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx219_get_format_code(imx219, codes[i]);
++
++ mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
++ width, height, fmt->format.width, fmt->format.height);
++ imx219_update_pad_format(imx219, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, state, fmt->pad);
++ *framefmt = fmt->format;
++ } else if (imx219->mode != mode ||
++ imx219->fmt.code != fmt->format.code) {
++ imx219->fmt = fmt->format;
++ imx219->mode = mode;
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
++ IMX219_VTS_MAX - mode->height, 1,
++ mode->vts_def - mode->height);
++ __v4l2_ctrl_s_ctrl(imx219->vblank, mode->vts_def - mode->height);
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = mode->vts_def - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum,
++ exposure_max, imx219->exposure->step, exposure_def);
++ /*
++ * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
++ * depends on mode->width only, and is not changeble in any
++ * way other than changing the mode.
++ */
++ hblank = IMX219_PPL_DEFAULT - mode->width;
++ __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank);
++ }
++
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_set_framefmt(struct imx219 *imx219)
++{
++ switch (imx219->fmt.code) {
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ return imx219_write_regs(imx219, raw8_framefmt_regs,
++ ARRAY_SIZE(raw8_framefmt_regs));
++
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ return imx219_write_regs(imx219, raw10_framefmt_regs,
++ ARRAY_SIZE(raw10_framefmt_regs));
++ }
++
++ return -EINVAL;
++}
++
++static const struct v4l2_rect *
++__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_state *state,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&imx219->sd, state, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &imx219->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int imx219_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct imx219 *imx219 = to_imx219(sd);
++
++ mutex_lock(&imx219->mutex);
++ sel->r = *__imx219_get_pad_crop(imx219, state, sel->pad, sel->which);
++ mutex_unlock(&imx219->mutex);
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.top = 0;
++ sel->r.left = 0;
++ sel->r.width = IMX219_NATIVE_WIDTH;
++ sel->r.height = IMX219_NATIVE_HEIGHT;
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ case V4L2_SEL_TGT_CROP_BOUNDS:
++ sel->r.top = IMX219_PIXEL_ARRAY_TOP;
++ sel->r.left = IMX219_PIXEL_ARRAY_LEFT;
++ sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
++ sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int imx219_start_streaming(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ const struct imx219_reg_list *reg_list;
++ int ret;
++
++ /* Apply default values of current mode */
++ reg_list = &imx219->mode->reg_list;
++ ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set mode\n", __func__);
++ goto err;
++ }
++
++ ret = imx219_set_framefmt(imx219);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set frame format: %d\n",
++ __func__, ret);
++ goto err;
++ }
++
++ /* Apply customized values from user */
++ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
++ if (ret)
++ goto err;
++
++ /* set stream on register */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++ if (ret)
++ goto err;
++
++ /* vflip and hflip cannot change during streaming */
++ __v4l2_ctrl_grab(imx219->vflip, true);
++ __v4l2_ctrl_grab(imx219->hflip, true);
++
++ return 0;
++
++err:
++ return ret;
++}
++
++static void imx219_stop_streaming(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++
++ /* set stream off register */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++ if (ret)
++ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++
++ __v4l2_ctrl_grab(imx219->vflip, false);
++ __v4l2_ctrl_grab(imx219->hflip, false);
++}
++
++static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ mutex_lock(&imx219->mutex);
++
++ if (enable) {
++ ret = pm_runtime_get_sync(&client->dev);
++ if (ret < 0) {
++ pm_runtime_put_noidle(&client->dev);
++ mutex_unlock(&imx219->mutex);
++ return ret;
++ }
++
++ if (imx219->streaming)
++ goto unlock;
++
++ /*
++ * Apply default & customized values
++ * and then start streaming.
++ */
++ ret = imx219_start_streaming(imx219);
++ if (ret)
++ goto err_unlock;
++ } else {
++ imx219_stop_streaming(imx219);
++ pm_runtime_put(&client->dev);
++ }
++
++unlock:
++ imx219->streaming += enable ? 1 : -1;
++ WARN_ON(imx219->streaming < 0);
++
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++
++err_unlock:
++ pm_runtime_put(&client->dev);
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++}
++
++/* Power/clock management functions */
++static int imx219_power_on(struct device *dev)
++{
++ struct v4l2_subdev *sd = dev_get_drvdata(dev);
++ struct imx219 *imx219 = to_imx219(sd);
++ int ret;
++
++ ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, imx219->supplies);
++ if (ret) {
++ dev_err(dev, "%s: failed to enable regulators\n",
++ __func__);
++ return ret;
++ }
++
++ ret = clk_prepare_enable(imx219->xclk);
++ if (ret) {
++ dev_err(dev, "%s: failed to enable clock\n", __func__);
++ goto reg_off;
++ }
++
++ gpiod_set_value_cansleep(imx219->reset_gpio, 1);
++ usleep_range(IMX219_XCLR_MIN_DELAY_US,
++ IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++ return 0;
++
++reg_off:
++ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++
++ return ret;
++}
++
++static int imx219_power_off(struct device *dev)
++{
++ struct v4l2_subdev *sd = dev_get_drvdata(dev);
++ struct imx219 *imx219 = to_imx219(sd);
++
++ gpiod_set_value_cansleep(imx219->reset_gpio, 0);
++ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++ clk_disable_unprepare(imx219->xclk);
++
++ return 0;
++}
++
++static int imx219_get_regulators(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ unsigned int i;
++
++ for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
++ imx219->supplies[i].supply = imx219_supply_name[i];
++
++ return devm_regulator_bulk_get(&client->dev,
++ IMX219_NUM_SUPPLIES, imx219->supplies);
++}
++
++/* Verify chip ID */
++static int imx219_identify_module(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++ u32 val;
++
++ ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
++ IMX219_REG_VALUE_16BIT, &val);
++ if (ret) {
++ dev_err(&client->dev, "failed to read chip id %x\n",
++ IMX219_CHIP_ID);
++ return ret;
++ }
++
++ if (val != IMX219_CHIP_ID) {
++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++ IMX219_CHIP_ID, val);
++ return -EIO;
++ }
++
++ dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, IMX219_CHIP_ID);
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx219_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx219_video_ops = {
++ .g_frame_interval = imx219_g_frame_interval,
++ .s_frame_interval = imx219_s_frame_interval,
++ .s_stream = imx219_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
++ .enum_mbus_code = imx219_enum_mbus_code,
++ .get_fmt = imx219_get_pad_format,
++ .set_fmt = imx219_set_pad_format,
++ .get_selection = imx219_get_selection,
++ .enum_frame_size = imx219_enum_frame_size,
++ .enum_frame_interval = imx219_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops imx219_subdev_ops = {
++ .core = &imx219_core_ops,
++ .video = &imx219_video_ops,
++ .pad = &imx219_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
++ .open = imx219_open,
++};
++
++/* Initialize control handlers */
++static int imx219_init_controls(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ struct v4l2_ctrl_handler *ctrl_hdlr;
++ unsigned int height = imx219->mode->height;
++ struct v4l2_fwnode_device_properties props;
++ int exposure_max, exposure_def, hblank;
++ int i, ret;
++
++ ctrl_hdlr = &imx219->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
++ if (ret)
++ return ret;
++
++ mutex_init(&imx219->mutex);
++ ctrl_hdlr->lock = &imx219->mutex;
++
++ /* By default, PIXEL_RATE is read only */
++ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_PIXEL_RATE, IMX219_PIXEL_RATE,
++ IMX219_PIXEL_RATE, 1, IMX219_PIXEL_RATE);
++
++ imx219->link_freq =
++ v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_LINK_FREQ,
++ ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, imx219_link_freq_menu);
++ if (imx219->link_freq)
++ imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++ /* Initial vblank/hblank/exposure parameters based on current mode */
++ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
++ IMX219_VTS_MAX - height, 1,
++ imx219->mode->vts_def - height);
++ hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
++ imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_HBLANK, hblank, hblank, 1, hblank);
++ if (imx219->hblank)
++ imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ exposure_max = imx219->mode->vts_def - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_EXPOSURE, IMX219_EXPOSURE_MIN, exposure_max,
++ IMX219_EXPOSURE_STEP, exposure_def);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
++ IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++ IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
++ IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
++
++ imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ if (imx219->hflip)
++ imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++ if (imx219->vflip)
++ imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(imx219_test_pattern_menu) - 1,
++ 0, 0, imx219_test_pattern_menu);
++ for (i = 0; i < 4; i++) {
++ /*
++ * The assumption is that
++ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2
++ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++ */
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_TEST_PATTERN_RED + i,
++ IMX219_TESTP_COLOUR_MIN,
++ IMX219_TESTP_COLOUR_MAX,
++ IMX219_TESTP_COLOUR_STEP,
++ IMX219_TESTP_COLOUR_MAX);
++ /* The "Solid color" pattern is white by default */
++ }
++
++ if (ctrl_hdlr->error) {
++ ret = ctrl_hdlr->error;
++ dev_err(&client->dev, "%s control init failed (%d)\n",
++ __func__, ret);
++ goto error;
++ }
++
++ ret = v4l2_fwnode_device_parse(&client->dev, &props);
++ if (ret)
++ goto error;
++
++ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, &props);
++ if (ret)
++ goto error;
++
++ imx219->sd.ctrl_handler = ctrl_hdlr;
++
++ return 0;
++
++error:
++ v4l2_ctrl_handler_free(ctrl_hdlr);
++ mutex_destroy(&imx219->mutex);
++
++ return ret;
++}
++
++static void imx219_free_controls(struct imx219 *imx219)
++{
++ v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
++ mutex_destroy(&imx219->mutex);
++}
++
++static int imx219_check_hwcfg(struct device *dev)
++{
++ struct fwnode_handle *endpoint;
++ struct v4l2_fwnode_endpoint ep_cfg = {
++ .bus_type = V4L2_MBUS_CSI2_DPHY
++ };
++ int ret = -EINVAL;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++ dev_err(dev, "could not parse endpoint\n");
++ goto error_out;
++ }
++
++ /* Check the number of MIPI CSI2 data lanes */
++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(dev, "only 2 data lanes are currently supported\n");
++ goto error_out;
++ }
++
++ /* Check the link frequency set in device tree */
++ if (!ep_cfg.nr_of_link_frequencies) {
++ dev_err(dev, "link-frequency property not found in DT\n");
++ goto error_out;
++ }
++
++ if (ep_cfg.nr_of_link_frequencies != 1 ||
++ ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
++ dev_err(dev, "Link frequency not supported: %lld\n",
++ ep_cfg.link_frequencies[0]);
++ goto error_out;
++ }
++
++ ret = 0;
++
++error_out:
++ v4l2_fwnode_endpoint_free(&ep_cfg);
++ fwnode_handle_put(endpoint);
++
++ return ret;
++}
++
++static int imx219_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct imx219 *imx219;
++ int ret;
++
++ imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
++ if (!imx219)
++ return -ENOMEM;
++
++ v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
++
++ /* Check the hardware configuration in device tree */
++ if (imx219_check_hwcfg(dev))
++ return -EINVAL;
++
++ /* Get system clock (xclk) */
++ imx219->xclk = devm_clk_get(dev, NULL);
++ if (IS_ERR(imx219->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(imx219->xclk);
++ }
++
++ imx219->xclk_freq = clk_get_rate(imx219->xclk);
++ if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
++ dev_err(dev, "xclk frequency not supported: %d Hz\n",
++ imx219->xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = imx219_get_regulators(imx219);
++ if (ret) {
++ dev_err(dev, "failed to get regulators\n");
++ return ret;
++ }
++
++ /* Request optional enable pin */
++ imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
++ usleep_range(IMX219_XCLR_MIN_DELAY_US,
++ IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++ /*
++ * The sensor must be powered for imx219_identify_module()
++ * to be able to read the CHIP_ID register
++ */
++ ret = imx219_power_on(dev);
++ if (ret)
++ return ret;
++
++ ret = imx219_identify_module(imx219);
++ if (ret)
++ goto error_power_off;
++
++ /* Set default mode to max resolution */
++ imx219->mode = &supported_modes[0];
++ imx219->frame_interval.numerator = 1;
++ imx219->frame_interval.denominator = supported_modes[0].fps;
++
++ /* sensor doesn't enter LP-11 state upon power up until and unless
++ * streaming is started, so upon power up switch the modes to:
++ * streaming -> standby
++ */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++ if (ret < 0)
++ goto error_power_off;
++ usleep_range(100, 110);
++
++ /* put sensor back to standby mode */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++ if (ret < 0)
++ goto error_power_off;
++ usleep_range(100, 110);
++
++ ret = imx219_init_controls(imx219);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize subdev */
++ imx219->sd.internal_ops = &imx219_internal_ops;
++ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++ imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ /* Initialize source pad */
++ imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++ /* Initialize default format */
++ imx219_set_default_format(imx219);
++
++ ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
++ if (ret) {
++ dev_err(dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ret = v4l2_async_register_subdev_sensor(&imx219->sd);
++ if (ret < 0) {
++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ /* Enable runtime PM and turn off the device */
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_idle(dev);
++
++ return 0;
++
++error_media_entity:
++ media_entity_cleanup(&imx219->sd.entity);
++
++error_handler_free:
++ imx219_free_controls(imx219);
++
++error_power_off:
++ imx219_power_off(dev);
++
++ return ret;
++}
++
++static void imx219_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ imx219_free_controls(imx219);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ imx219_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id imx219_dt_ids[] = {
++ { .compatible = "sony,imx219" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx219_dt_ids);
++
++static const struct dev_pm_ops imx219_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
++ SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
++};
++
++static struct i2c_driver imx219_i2c_driver = {
++ .driver = {
++ .name = "imx219",
++ .of_match_table = imx219_dt_ids,
++ .pm = &imx219_pm_ops,
++ },
++ .probe = imx219_probe,
++ .remove = imx219_remove,
++};
++
++module_i2c_driver(imx219_i2c_driver);
++
++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
++MODULE_DESCRIPTION("Sony IMX219 sensor driver");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
+@@ -0,0 +1,1921 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++#define OV13850_XCLK_MIN 6000000
++#define OV13850_XCLK_MAX 54000000
++
++#define OV13850_LINK_FREQ_500MHZ 500000000LL
++
++/**
++ *OV13850 PLL
++ *
++ *PLL1:
++ *
++ *REF_CLK -> /PREDIVP[0] -> /PREDIV[2:0] -> *DIVP[9:8,7:0] -> /DIVM[3:0] -> /DIV_MIPI[1:0] -> PCLK
++ *(6-64M) 0x030A 0x0300 0x0301,0x302 0x0303 0x0304
++ * `-> MIPI_PHY_CLK
++ *
++ *
++ *PLL2:
++ * 000: 1
++ * 001: 1.5
++ * 010: 2
++ * 011: 2.5
++ * 100: 3
++ * 101: 4
++ * 0: /1 110: 6
++ * 1: /2 111: 8
++ *REF_CLK -> /PREDIVP[3] -> /PREDIV[2:0] -> /DIVP[9:0] -> /DIVDAC[3:0] -> DAC_CLK =
++ *(6~64M) 0x3611
++ * -> /DIVSP[3:0] -> /DIVS[2:0] -> SCLK
++ *
++ * -> /(1+DIVSRAM[3:0]) -> SRAM_CLK
++ */
++
++// PREDIVP
++#define OV13850_REG_PLL1_PREDIVP 0x030a
++#define OV13850_PREDIVP_1 0
++#define OV13850_PREDIVP_2 1
++
++// PREDIV
++#define OV13850_REG_PLL1_PREDIV 0x0300
++#define OV13850_PREDIV_1 0
++#define OV13850_PREDIV_1_5 1
++#define OV13850_PREDIV_2 2
++#define OV13850_PREDIV_2_5 3
++#define OV13850_PREDIV_3 4
++#define OV13850_PREDIV_4 5
++#define OV13850_PREDIV_6 6
++#define OV13850_PREDIV_8 7
++
++// DIVP
++#define OV13850_REG_PLL1_DIVP_H 0x0301
++#define OV13850_REG_PLL1_DIVP_L 0x0302
++#define OV13850_REG_PLL1_DIVP OV13850_REG_PLL1_DIVP_H
++
++// DIVM
++#define OV13850_REG_PLL1_DIVM 0x0303
++#define OV13850_DIVM(n) ((n)-1) // n=1~16
++
++// DIV_MIPI
++#define OV13850_REG_PLL1_DIV_MIPI 0x0304
++#define OV13850_DIV_MIPI_4 0
++#define OV13850_DIV_MIPI_5 1
++#define OV13850_DIV_MIPI_6 2
++#define OV13850_DIV_MIPI_8 3
++
++// system control
++#define OV13850_STREAM_CTRL 0x0100
++#define OV13850_REG_MIPI_SC 0x300f
++#define OV13850_MIPI_SC_8_BIT 0x0
++#define OV13850_MIPI_SC_10_BIT 0x1
++#define OV13850_MIPI_SC_12_BIT 0x2
++#define OV13850_GET_MIPI_SC_MIPI_BIT(v) ((v) & 0x3)
++#define OV13850_REG_MIPI_SC_CTRL0 0x3012
++#define OV13850_GET_MIPI_SC_CTRL0_LANE_NUM(v) ((v)>>4 & 0xf)
++
++// timing
++#define OV13850_REG_H_CROP_START_H 0x3800
++#define OV13850_REG_H_CROP_START_L 0x3801
++#define OV13850_REG_H_CROP_START OV13850_REG_H_CROP_START_H
++#define OV13850_REG_V_CROP_START_H 0x3802
++#define OV13850_REG_V_CROP_START_L 0x3803
++#define OV13850_REG_V_CROP_START OV13850_REG_V_CROP_START_H
++
++#define OV13850_REG_H_CROP_END_H 0x3804
++#define OV13850_REG_H_CROP_END_L 0x3805
++#define OV13850_REG_H_CROP_END OV13850_REG_H_CROP_END_H
++#define OV13850_REG_V_CROP_END_H 0x3806
++#define OV13850_REG_V_CROP_END_L 0x3807
++#define OV13850_REG_V_CROP_END OV13850_REG_V_CROP_END_H
++
++#define OV13850_REG_H_OUTPUT_SIZE_H 0x3808
++#define OV13850_REG_H_OUTPUT_SIZE_L 0x3809
++#define OV13850_REG_H_OUTPUT_SIZE OV13850_REG_H_OUTPUT_SIZE_H
++#define OV13850_REG_V_OUTPUT_SIZE_H 0x380a
++#define OV13850_REG_V_OUTPUT_SIZE_L 0x380b
++#define OV13850_REG_V_OUTPUT_SIZE OV13850_REG_V_OUTPUT_SIZE_H
++
++#define OV13850_REG_TIMING_HTS_H 0x380c
++#define OV13850_REG_TIMING_HTS_L 0x380d
++#define OV13850_REG_TIMING_HTS OV13850_REG_TIMING_HTS_H
++#define OV13850_REG_TIMING_VTS_H 0x380e
++#define OV13850_REG_TIMING_VTS_L 0x380f
++#define OV13850_REG_TIMING_VTS OV13850_REG_TIMING_VTS_H
++
++
++#define OV13850_REG_H_WIN_OFF_H 0x3810
++#define OV13850_REG_H_WIN_OFF_L 0x3811
++#define OV13850_REG_V_WIN_OFF_H 0x3812
++#define OV13850_REG_V_WIN_OFF_L 0x3813
++
++#define OV13850_REG_H_INC 0x3814
++#define OV13850_REG_V_INC 0x3815
++
++enum ov13850_mode_id {
++ OV13850_MODE_1080P_1920_1080 = 0,
++ OV13850_NUM_MODES,
++};
++
++enum ov13850_frame_rate {
++ OV13850_15_FPS = 0,
++ OV13850_30_FPS,
++ OV13850_60_FPS,
++ OV13850_NUM_FRAMERATES,
++};
++
++static const int ov13850_framerates[] = {
++ [OV13850_15_FPS] = 15,
++ [OV13850_30_FPS] = 30,
++ [OV13850_60_FPS] = 60,
++};
++
++struct ov13850_pixfmt {
++ u32 code;
++ u32 colorspace;
++};
++
++static const struct ov13850_pixfmt ov13850_formats[] = {
++ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, },
++};
++
++/* regulator supplies */
++static const char * const ov13850_supply_name[] = {
++ "DOVDD", /* Digital I/O (1.8V) supply */
++ "AVDD", /* Analog (2.8V) supply */
++ "DVDD", /* Digital Core (1.5V) supply */
++};
++
++#define OV13850_NUM_SUPPLIES ARRAY_SIZE(ov13850_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov13850_downsize_mode {
++ SUBSAMPLING,
++ SCALING,
++};
++
++struct reg_value {
++ u16 reg_addr;
++ u8 val;
++ u8 mask;
++ u32 delay_ms;
++};
++
++struct ov13850_mode_info {
++ enum ov13850_mode_id id;
++ enum ov13850_downsize_mode dn_mode;
++ u32 hact;
++ u32 htot;
++ u32 vact;
++ u32 vtot;
++ const struct reg_value *reg_data;
++ u32 reg_data_size;
++ u32 max_fps;
++};
++
++struct ov13850_ctrls {
++ struct v4l2_ctrl_handler handler;
++ struct v4l2_ctrl *pixel_rate;
++ struct {
++ struct v4l2_ctrl *exposure;
++ };
++ struct {
++ struct v4l2_ctrl *auto_wb;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *red_balance;
++ };
++ struct {
++ struct v4l2_ctrl *anal_gain;
++ };
++ struct v4l2_ctrl *brightness;
++ struct v4l2_ctrl *light_freq;
++ struct v4l2_ctrl *link_freq;
++ struct v4l2_ctrl *saturation;
++ struct v4l2_ctrl *contrast;
++ struct v4l2_ctrl *hue;
++ struct v4l2_ctrl *test_pattern;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vflip;
++};
++
++struct ov13850_dev {
++ struct i2c_client *i2c_client;
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++ struct clk *xclk; /* system clock to OV13850 */
++ u32 xclk_freq;
++
++ struct regulator_bulk_data supplies[OV13850_NUM_SUPPLIES];
++ struct gpio_desc *reset_gpio;
++ struct gpio_desc *pwdn_gpio;
++ bool upside_down;
++
++ /* lock to protect all members below */
++ struct mutex lock;
++
++ int power_count;
++
++ struct v4l2_mbus_framefmt fmt;
++ bool pending_fmt_change;
++
++ const struct ov13850_mode_info *current_mode;
++ const struct ov13850_mode_info *last_mode;
++ enum ov13850_frame_rate current_fr;
++ struct v4l2_fract frame_interval;
++
++ struct ov13850_ctrls ctrls;
++
++ u32 prev_sysclk, prev_hts;
++ u32 ae_low, ae_high, ae_target;
++
++ bool pending_mode_change;
++ bool streaming;
++};
++
++static inline struct ov13850_dev *to_ov13850_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ov13850_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct ov13850_dev,
++ ctrls.handler)->sd;
++}
++
++/* ov13850 initial register */
++static const struct reg_value ov13850_init_setting_30fps_1080P[] = {
++
++};
++
++static const struct reg_value ov13850_setting_1080P_1920_1080[] = {
++//;XVCLK=24Mhz, SCLK=4x120Mhz, MIPI 640Mbps, DACCLK=240Mhz
++/*
++ * using quarter size to scale down
++ */
++ {0x0103, 0x01, 0, 0}, // ; software reset
++
++ {0x0300, 0x01, 0, 0}, //; PLL
++ {0x0301, 0x00, 0, 0}, //; PLL1_DIVP_hi
++ {0x0302, 0x28, 0, 0}, //; PLL1_DIVP_lo
++ {0x0303, 0x00, 0, 0}, // ; PLL
++ {0x030a, 0x00, 0, 0}, // ; PLL
++ //{0xffff, 20, 0, 0},
++ {0x300f, 0x11, 0, 0}, // SFC modified, MIPI_SRC, [1:0] 00-8bit, 01-10bit, 10-12bit
++ {0x3010, 0x01, 0, 0}, // ; MIPI PHY
++ {0x3011, 0x76, 0, 0}, // ; MIPI PHY
++ {0x3012, 0x41, 0, 0}, // ; MIPI 4 lane
++ {0x3013, 0x12, 0, 0}, // ; MIPI control
++ {0x3014, 0x11, 0, 0}, // ; MIPI control
++ {0x301f, 0x03, 0, 0}, //
++ {0x3106, 0x00, 0, 0}, //
++ {0x3210, 0x47, 0, 0}, //
++ {0x3500, 0x00, 0, 0}, // ; exposure HH
++ {0x3501, 0x67, 0, 0}, // ; exposure H
++ {0x3502, 0x80, 0, 0}, // ; exposure L
++ {0x3506, 0x00, 0, 0}, // ; short exposure HH
++ {0x3507, 0x02, 0, 0}, // ; short exposure H
++ {0x3508, 0x00, 0, 0}, // ; shour exposure L
++ {0x3509, 0x10, 0, 0},//00},//8},
++ {0x350a, 0x00, 0, 0}, // ; gain H
++ {0x350b, 0x10, 0, 0}, // ; gain L
++ {0x350e, 0x00, 0, 0}, // ; short gain H
++ {0x350f, 0x10, 0, 0}, // ; short gain L
++ {0x3600, 0x40, 0, 0}, // ; analog control
++ {0x3601, 0xfc, 0, 0}, // ; analog control
++ {0x3602, 0x02, 0, 0}, // ; analog control
++ {0x3603, 0x48, 0, 0}, // ; analog control
++ {0x3604, 0xa5, 0, 0}, // ; analog control
++ {0x3605, 0x9f, 0, 0}, // ; analog control
++ {0x3607, 0x00, 0, 0}, // ; analog control
++ {0x360a, 0x40, 0, 0}, // ; analog control
++ {0x360b, 0x91, 0, 0}, // ; analog control
++ {0x360c, 0x49, 0, 0}, // ; analog control
++ {0x360f, 0x8a, 0, 0}, //
++ {0x3611, 0x10, 0, 0}, // ; PLL2
++ //{0x3612, 0x23, 0, 0}, // ; PLL2
++ {0x3612, 0x13, 0, 0}, // ; PLL2
++ //{0x3613, 0x33, 0, 0}, // ; PLL2
++ {0x3613, 0x22, 0, 0}, // ; PLL2
++ //{0xffff, 50, 0, 0},
++ {0x3614, 0x28, 0, 0}, //[7:0] PLL2_DIVP lo
++ {0x3615, 0x08, 0, 0}, //[7:6] Debug mode, [5:4] N_pump clock div, [3:2] P_pump clock div, [1:0] PLL2_DIVP hi
++ {0x3641, 0x02, 0, 0},
++ {0x3660, 0x82, 0, 0},
++ {0x3668, 0x54, 0, 0},
++ {0x3669, 0x40, 0, 0},
++ {0x3667, 0xa0, 0, 0},
++ {0x3702, 0x40, 0, 0},
++ {0x3703, 0x44, 0, 0},
++ {0x3704, 0x2c, 0, 0},
++ {0x3705, 0x24, 0, 0},
++ {0x3706, 0x50, 0, 0},
++ {0x3707, 0x44, 0, 0},
++ {0x3708, 0x3c, 0, 0},
++ {0x3709, 0x1f, 0, 0},
++ {0x370a, 0x26, 0, 0},
++ {0x370b, 0x3c, 0, 0},
++ {0x3720, 0x66, 0, 0},
++ {0x3722, 0x84, 0, 0},
++ {0x3728, 0x40, 0, 0},
++ {0x372a, 0x00, 0, 0},
++ {0x372f, 0x90, 0, 0},
++ {0x3710, 0x28, 0, 0},
++ {0x3716, 0x03, 0, 0},
++ {0x3718, 0x10, 0, 0},
++ {0x3719, 0x08, 0, 0},
++ {0x371c, 0xfc, 0, 0},
++ {0x3760, 0x13, 0, 0},
++ {0x3761, 0x34, 0, 0},
++ {0x3767, 0x24, 0, 0},
++ {0x3768, 0x06, 0, 0},
++ {0x3769, 0x45, 0, 0},
++ {0x376c, 0x23, 0, 0},
++ {0x3d84, 0x00, 0, 0}, // ; OTP program disable
++ {0x3d85, 0x17, 0, 0}, // ; OTP power up load data enable, power load setting enable, software load setting
++ {0x3d8c, 0x73, 0, 0}, // ; OTP start address H
++ {0x3d8d, 0xbf, 0, 0}, // ; OTP start address L
++ {0x3800, 0x00, 0, 0}, // ; H crop start H
++ {0x3801, 0x08, 0, 0}, // ; H crop start L
++ {0x3802, 0x00, 0, 0}, // ; V crop start H
++ {0x3803, 0x04, 0, 0}, // ; V crop start L
++ {0x3804, 0x10, 0, 0}, // ; H crop end H
++ {0x3805, 0x97, 0, 0}, // ; H crop end L
++ {0x3806, 0x0c, 0, 0}, // ; V crop end H
++ {0x3807, 0x4b, 0, 0}, // ; V crop end L
++ {0x3808, 0x08, 0, 0}, // ; H output size H
++ {0x3809, 0x40, 0, 0}, // ; H output size L
++ {0x380a, 0x06, 0, 0}, // ; V output size H
++ {0x380b, 0x20, 0, 0}, // ; V output size L
++ {0x380c, 0x25, 0, 0}, // ; HTS H
++ {0x380d, 0x80, 0, 0}, // ; HTS L
++ {0x380e, 0x06, 0, 0}, // ; VTS H
++ {0x380f, 0x80, 0, 0}, // ; VTS L
++ {0x3810, 0x00, 0, 0}, // ; H win off H
++ {0x3811, 0x04, 0, 0}, // ; H win off L
++ {0x3812, 0x00, 0, 0}, // ; V win off H
++ {0x3813, 0x02, 0, 0}, // ; V win off L
++ {0x3814, 0x31, 0, 0}, // ; H inc
++ {0x3815, 0x31, 0, 0}, // ; V inc
++ {0x3820, 0x02, 0, 0}, // ; V flip off, V bin on
++ {0x3821, 0x05, 0, 0}, // ; H mirror on, H bin on
++ {0x3834, 0x00, 0, 0}, //
++ {0x3835, 0x1c, 0, 0}, // ; cut_en, vts_auto, blk_col_dis
++ {0x3836, 0x08, 0, 0}, //
++ {0x3837, 0x02, 0, 0}, //
++ {0x4000, 0xf1, 0, 0},//c1}, // ; BLC offset trig en, format change trig en, gain trig en, exp trig en, median en
++ {0x4001, 0x00, 0, 0}, // ; BLC
++ {0x400b, 0x0c, 0, 0}, // ; BLC
++ {0x4011, 0x00, 0, 0}, // ; BLC
++ {0x401a, 0x00, 0, 0}, // ; BLC
++ {0x401b, 0x00, 0, 0}, // ; BLC
++ {0x401c, 0x00, 0, 0}, // ; BLC
++ {0x401d, 0x00, 0, 0}, // ; BLC
++ {0x4020, 0x00, 0, 0}, // ; BLC
++ {0x4021, 0xe4, 0, 0}, // ; BLC
++ {0x4022, 0x07, 0, 0}, // ; BLC
++ {0x4023, 0x5f, 0, 0}, // ; BLC
++ {0x4024, 0x08, 0, 0}, // ; BLC
++ {0x4025, 0x44, 0, 0}, // ; BLC
++ {0x4026, 0x08, 0, 0}, // ; BLC
++ {0x4027, 0x47, 0, 0}, // ; BLC
++ {0x4028, 0x00, 0, 0}, // ; BLC
++ {0x4029, 0x02, 0, 0}, // ; BLC
++ {0x402a, 0x04, 0, 0}, // ; BLC
++ {0x402b, 0x08, 0, 0}, // ; BLC
++ {0x402c, 0x02, 0, 0}, // ; BLC
++ {0x402d, 0x02, 0, 0}, // ; BLC
++ {0x402e, 0x0c, 0, 0}, // ; BLC
++ {0x402f, 0x08, 0, 0}, // ; BLC
++ {0x403d, 0x2c, 0, 0}, //
++ {0x403f, 0x7f, 0, 0}, //
++ {0x4500, 0x82, 0, 0}, // ; BLC
++ {0x4501, 0x38, 0, 0}, // ; BLC
++ {0x4601, 0x04, 0, 0}, //
++ {0x4602, 0x22, 0, 0}, //
++ {0x4603, 0x01, 0, 0}, //; VFIFO
++ {0x4837, 0x19, 0, 0}, //; MIPI global timing
++ {0x4d00, 0x04, 0, 0}, // ; temperature monitor
++ {0x4d01, 0x42, 0, 0}, // ; temperature monitor
++ {0x4d02, 0xd1, 0, 0}, // ; temperature monitor
++ {0x4d03, 0x90, 0, 0}, // ; temperature monitor
++ {0x4d04, 0x66, 0, 0}, // ; temperature monitor
++ {0x4d05, 0x65, 0, 0}, // ; temperature monitor
++ {0x5000, 0x0e, 0, 0}, // ; windowing enable, BPC on, WPC on, Lenc on
++ {0x5001, 0x03, 0, 0}, // ; BLC enable, MWB on
++ {0x5002, 0x07, 0, 0}, //
++ {0x5013, 0x40, 0, 0},
++ {0x501c, 0x00, 0, 0},
++ {0x501d, 0x10, 0, 0},
++ //{0x5057, 0x56, 0, 0},//add
++ {0x5056, 0x08, 0, 0},
++ {0x5058, 0x08, 0, 0},
++ {0x505a, 0x08, 0, 0},
++ {0x5242, 0x00, 0, 0},
++ {0x5243, 0xb8, 0, 0},
++ {0x5244, 0x00, 0, 0},
++ {0x5245, 0xf9, 0, 0},
++ {0x5246, 0x00, 0, 0},
++ {0x5247, 0xf6, 0, 0},
++ {0x5248, 0x00, 0, 0},
++ {0x5249, 0xa6, 0, 0},
++ {0x5300, 0xfc, 0, 0},
++ {0x5301, 0xdf, 0, 0},
++ {0x5302, 0x3f, 0, 0},
++ {0x5303, 0x08, 0, 0},
++ {0x5304, 0x0c, 0, 0},
++ {0x5305, 0x10, 0, 0},
++ {0x5306, 0x20, 0, 0},
++ {0x5307, 0x40, 0, 0},
++ {0x5308, 0x08, 0, 0},
++ {0x5309, 0x08, 0, 0},
++ {0x530a, 0x02, 0, 0},
++ {0x530b, 0x01, 0, 0},
++ {0x530c, 0x01, 0, 0},
++ {0x530d, 0x0c, 0, 0},
++ {0x530e, 0x02, 0, 0},
++ {0x530f, 0x01, 0, 0},
++ {0x5310, 0x01, 0, 0},
++ {0x5400, 0x00, 0, 0},
++ {0x5401, 0x61, 0, 0},
++ {0x5402, 0x00, 0, 0},
++ {0x5403, 0x00, 0, 0},
++ {0x5404, 0x00, 0, 0},
++ {0x5405, 0x40, 0, 0},
++ {0x540c, 0x05, 0, 0},
++ {0x5b00, 0x00, 0, 0},
++ {0x5b01, 0x00, 0, 0},
++ {0x5b02, 0x01, 0, 0},
++ {0x5b03, 0xff, 0, 0},
++ {0x5b04, 0x02, 0, 0},
++ {0x5b05, 0x6c, 0, 0},
++ {0x5b09, 0x02, 0, 0}, //
++ //{0x5e00, 0x00, 0, 0}, // ; test pattern disable
++ //{0x5e00, 0x80, 0, 0}, // ; test pattern enable
++ {0x5e10, 0x1c, 0, 0}, // ; ISP test disable
++
++ //{0x0300, 0x01, 0, 0},// ; PLL
++ //{0x0302, 0x28, 0, 0},// ; PLL
++ //{0xffff, 50, 0, 0},
++ {0x3501, 0x67, 0, 0},// ; Exposure H
++ {0x370a, 0x26, 0, 0},//
++ {0x372a, 0x00, 0, 0},
++ {0x372f, 0x90, 0, 0},
++ {0x3801, 0x08, 0, 0}, //; H crop start L
++ {0x3803, 0x04, 0, 0}, //; V crop start L
++ {0x3805, 0x97, 0, 0}, //; H crop end L
++ {0x3807, 0x4b, 0, 0}, //; V crop end L
++ {0x3808, 0x08, 0, 0}, //; H output size H
++ {0x3809, 0x40, 0, 0}, //; H output size L
++ {0x380a, 0x06, 0, 0}, //; V output size H
++ {0x380b, 0x20, 0, 0}, //; V output size L
++ {0x380c, 0x25, 0, 0}, //; HTS H
++ {0x380d, 0x80, 0, 0}, //; HTS L
++ {0x380e, 0x0a, 0, 0},//6}, //; VTS H
++ {0x380f, 0x80, 0, 0}, //; VTS L
++ {0x3813, 0x02, 0, 0}, //; V win off
++ {0x3814, 0x31, 0, 0}, //; H inc
++ {0x3815, 0x31, 0, 0}, //; V inc
++ {0x3820, 0x02, 0, 0}, //; V flip off, V bin on
++ {0x3821, 0x05, 0, 0}, //; H mirror on, H bin on
++ {0x3836, 0x08, 0, 0}, //
++ {0x3837, 0x02, 0, 0}, //
++ {0x4020, 0x00, 0, 0}, //
++ {0x4021, 0xe4, 0, 0}, //
++ {0x4022, 0x07, 0, 0}, //
++ {0x4023, 0x5f, 0, 0}, //
++ {0x4024, 0x08, 0, 0}, //
++ {0x4025, 0x44, 0, 0}, //
++ {0x4026, 0x08, 0, 0}, //
++ {0x4027, 0x47, 0, 0}, //
++ {0x4603, 0x01, 0, 0}, //; VFIFO
++ {0x4837, 0x19, 0, 0}, //; MIPI global timing
++ {0x4802, 0x42, 0, 0}, //default 0x00
++ {0x481a, 0x00, 0, 0},
++ {0x481b, 0x1c, 0, 0}, //default 0x3c prepare
++ {0x4826, 0x12, 0, 0}, //default 0x32 trail
++ {0x5401, 0x61, 0, 0}, //
++ {0x5405, 0x40, 0, 0}, //
++
++ //{0xffff, 200, 0, 0},
++ //{0xffff, 200, 0, 0},
++ //{0xffff, 200, 0, 0},
++
++ //{0x0100, 0x01, 0, 0}, //; wake up, streaming
++};
++
++/* power-on sensor init reg table */
++static const struct ov13850_mode_info ov13850_mode_init_data = {
++ OV13850_MODE_1080P_1920_1080, SCALING,
++ 1920, 0x6e0, 1080, 0x470,
++ ov13850_init_setting_30fps_1080P,
++ ARRAY_SIZE(ov13850_init_setting_30fps_1080P),
++ OV13850_30_FPS,
++};
++
++static const struct ov13850_mode_info
++ov13850_mode_data[OV13850_NUM_MODES] = {
++ {OV13850_MODE_1080P_1920_1080, SCALING,
++ 1920, 0x6e0, 1080, 0x470,
++ ov13850_setting_1080P_1920_1080,
++ ARRAY_SIZE(ov13850_setting_1080P_1920_1080),
++ OV13850_30_FPS},
++};
++
++static int ov13850_write_reg(struct ov13850_dev *sensor, u16 reg, u8 val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg;
++ u8 buf[3];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val;
++
++ msg.addr = client->addr;
++ msg.flags = client->flags;
++ msg.buf = buf;
++ msg.len = sizeof(buf);
++
++ ret = i2c_transfer(client->adapter, &msg, 1);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++ __func__, reg, val);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ov13850_read_reg(struct ov13850_dev *sensor, u16 reg, u8 *val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg[2];
++ u8 buf[2];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++
++ msg[0].addr = client->addr;
++ msg[0].flags = client->flags;
++ msg[0].buf = buf;
++ msg[0].len = sizeof(buf);
++
++ msg[1].addr = client->addr;
++ msg[1].flags = client->flags | I2C_M_RD;
++ msg[1].buf = buf;
++ msg[1].len = 1;
++
++ ret = i2c_transfer(client->adapter, msg, 2);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x\n",
++ __func__, reg);
++ return ret;
++ }
++
++ *val = buf[0];
++ return 0;
++}
++
++static int ov13850_read_reg16(struct ov13850_dev *sensor, u16 reg, u16 *val)
++{
++ u8 hi, lo;
++ int ret;
++
++ ret = ov13850_read_reg(sensor, reg, &hi);
++ if (ret)
++ return ret;
++ ret = ov13850_read_reg(sensor, reg + 1, &lo);
++ if (ret)
++ return ret;
++
++ *val = ((u16)hi << 8) | (u16)lo;
++ return 0;
++}
++
++static int ov13850_write_reg16(struct ov13850_dev *sensor, u16 reg, u16 val)
++{
++ int ret;
++
++ ret = ov13850_write_reg(sensor, reg, val >> 8);
++ if (ret)
++ return ret;
++
++ return ov13850_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov13850_mod_reg(struct ov13850_dev *sensor, u16 reg,
++ u8 mask, u8 val)
++{
++ u8 readval;
++ int ret;
++
++ ret = ov13850_read_reg(sensor, reg, &readval);
++ if (ret)
++ return ret;
++
++ readval &= ~mask;
++ val &= mask;
++ val |= readval;
++
++ return ov13850_write_reg(sensor, reg, val);
++}
++
++static int ov13850_set_timings(struct ov13850_dev *sensor,
++ const struct ov13850_mode_info *mode)
++{
++ int ret;
++
++ ret = ov13850_write_reg16(sensor, OV13850_REG_H_OUTPUT_SIZE, mode->hact);
++ if (ret < 0)
++ return ret;
++
++ ret = ov13850_write_reg16(sensor, OV13850_REG_V_OUTPUT_SIZE, mode->vact);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int ov13850_load_regs(struct ov13850_dev *sensor,
++ const struct ov13850_mode_info *mode)
++{
++ const struct reg_value *regs = mode->reg_data;
++ unsigned int i;
++ u32 delay_ms;
++ u16 reg_addr;
++ u8 mask, val;
++ int ret = 0;
++
++ st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++ for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++ delay_ms = regs->delay_ms;
++ reg_addr = regs->reg_addr;
++ val = regs->val;
++ mask = regs->mask;
++
++ if (mask)
++ ret = ov13850_mod_reg(sensor, reg_addr, mask, val);
++ else
++ ret = ov13850_write_reg(sensor, reg_addr, val);
++ if (ret)
++ break;
++
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++
++ return ov13850_set_timings(sensor, mode);
++}
++
++
++
++static int ov13850_get_gain(struct ov13850_dev *sensor)
++{
++ u32 gain = 0;
++ return gain;
++}
++
++static int ov13850_set_gain(struct ov13850_dev *sensor, int gain)
++{
++ return 0;
++}
++
++static int ov13850_set_stream_mipi(struct ov13850_dev *sensor, bool on)
++{
++ return 0;
++}
++
++static int ov13850_get_sysclk(struct ov13850_dev *sensor)
++{
++ return 0;
++}
++
++static int ov13850_set_night_mode(struct ov13850_dev *sensor)
++{
++ return 0;
++}
++
++static int ov13850_get_hts(struct ov13850_dev *sensor)
++{
++ /* read HTS from register settings */
++ u16 hts;
++ int ret;
++
++ ret = ov13850_read_reg16(sensor, OV13850_REG_TIMING_HTS, &hts);
++ if (ret)
++ return ret;
++ return hts;
++}
++
++static int ov13850_set_hts(struct ov13850_dev *sensor, int hts)
++{
++ return ov13850_write_reg16(sensor, OV13850_REG_TIMING_HTS, hts);
++}
++
++
++static int ov13850_get_vts(struct ov13850_dev *sensor)
++{
++ u16 vts;
++ int ret;
++
++ ret = ov13850_read_reg16(sensor, OV13850_REG_TIMING_VTS, &vts);
++ if (ret)
++ return ret;
++ return vts;
++}
++
++static int ov13850_set_vts(struct ov13850_dev *sensor, int vts)
++{
++ return ov13850_write_reg16(sensor, OV13850_REG_TIMING_VTS, vts);
++}
++
++static int ov13850_get_light_freq(struct ov13850_dev *sensor)
++{
++ return 0;
++}
++
++static int ov13850_set_bandingfilter(struct ov13850_dev *sensor)
++{
++ return 0;
++}
++
++static int ov13850_set_ae_target(struct ov13850_dev *sensor, int target)
++{
++ return 0;
++}
++
++static int ov13850_get_binning(struct ov13850_dev *sensor)
++{
++ return 0;
++}
++
++static int ov13850_set_binning(struct ov13850_dev *sensor, bool enable)
++{
++ return 0;
++}
++
++static const struct ov13850_mode_info *
++ov13850_find_mode(struct ov13850_dev *sensor, enum ov13850_frame_rate fr,
++ int width, int height, bool nearest)
++{
++ const struct ov13850_mode_info *mode;
++
++ mode = v4l2_find_nearest_size(ov13850_mode_data,
++ ARRAY_SIZE(ov13850_mode_data),
++ hact, vact,
++ width, height);
++
++ if (!mode ||
++ (!nearest && (mode->hact != width || mode->vact != height)))
++ return NULL;
++
++ /* Check to see if the current mode exceeds the max frame rate */
++ if (ov13850_framerates[fr] > ov13850_framerates[mode->max_fps])
++ return NULL;
++
++ return mode;
++}
++
++static u64 ov13850_calc_pixel_rate(struct ov13850_dev *sensor)
++{
++ u64 rate;
++
++ rate = sensor->current_mode->vact * sensor->current_mode->hact;
++ rate *= ov13850_framerates[sensor->current_fr];
++
++ return rate;
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ * +--------------+
++ * | Ext. Clock |
++ * +-+------------+
++ * | +----------+
++ * +->| PLL1 | - reg 0x030a, bit0 for the pre-dividerp
++ * +-+--------+ - reg 0x0300, bits 0-2 for the pre-divider
++ * +-+--------+ - reg 0x0301~0x0302, for the multiplier
++ * | +--------------+
++ * +->| MIPI Divider | - reg 0x0303, bits 0-3 for the pre-divider
++ * | +---------> MIPI PHY CLK
++ * | +-----+
++ * | +->| PLL1_DIV_MIPI | - reg 0x0304, bits 0-1 for the divider
++ * | +----------------> PCLK
++ * | +-----+
++ *
++ * +--------------+
++ * | Ext. Clock |
++ * +-+------------+
++ * | +----------+
++ * +->| PLL2 | - reg 0x0311, bit0 for the pre-dividerp
++ * +-+--------+ - reg 0x030b, bits 0-2 for the pre-divider
++ * +-+--------+ - reg 0x030c~0x030d, for the multiplier
++ * | +--------------+
++ * +->| SCLK Divider | - reg 0x030F, bits 0-3 for the pre-divider
++ * +-+--------+ - reg 0x030E, bits 0-2 for the divider
++ * | +---------> SCLK
++ *
++ * | +-----+
++ * +->| DAC Divider | - reg 0x0312, bits 0-3 for the divider
++ * | +----------------> DACCLK
++ **
++ */
++
++/*
++ * ov13850_set_mipi_pclk() - Calculate the clock tree configuration values
++ * for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ * 'Bandwidth Per Lane' is calculated as:
++ * bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ *
++ * - mipi_pclk = bpl / 2; ( / 2 is for CSI-2 DDR)
++ * - mipi_phy_clk = mipi_pclk * PLL1_DIV_MIPI;
++ *
++ * with these fixed parameters:
++ * PLL1_PREDIVP = 1;
++ * PLL1_PREDIV = 1; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ * PLL1_DIVM = 1;
++ * PLL1_DIV_MIPI = 4;
++ *
++ * FIXME: this have been tested with 10-bit raw and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++
++
++
++static int ov13850_set_mipi_pclk(struct ov13850_dev *sensor,
++ unsigned long rate)
++{
++
++ return 0;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov13850_set_mode_direct(struct ov13850_dev *sensor,
++ const struct ov13850_mode_info *mode)
++{
++ if (!mode->reg_data)
++ return -EINVAL;
++
++ /* Write capture setting */
++ return ov13850_load_regs(sensor, mode);
++}
++
++static int ov13850_set_mode(struct ov13850_dev *sensor)
++{
++ const struct ov13850_mode_info *mode = sensor->current_mode;
++ const struct ov13850_mode_info *orig_mode = sensor->last_mode;
++ int ret = 0;
++
++ ret = ov13850_set_mode_direct(sensor, mode);
++ if (ret < 0)
++ return ret;
++
++ /*
++ * we support have 10 bits raw RGB(mipi)
++ */
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++ ret = ov13850_set_mipi_pclk(sensor, 0);
++
++ if (ret < 0)
++ return 0;
++
++ sensor->pending_mode_change = false;
++ sensor->last_mode = mode;
++ return 0;
++}
++
++static int ov13850_set_framefmt(struct ov13850_dev *sensor,
++ struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int ov13850_restore_mode(struct ov13850_dev *sensor)
++{
++ int ret;
++
++ /* first load the initial register values */
++ ret = ov13850_load_regs(sensor, &ov13850_mode_init_data);
++ if (ret < 0)
++ return ret;
++ sensor->last_mode = &ov13850_mode_init_data;
++
++ /* now restore the last capture mode */
++ ret = ov13850_set_mode(sensor);
++ if (ret < 0)
++ return ret;
++
++ return ov13850_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void ov13850_power(struct ov13850_dev *sensor, bool enable)
++{
++ if (!sensor->pwdn_gpio)
++ return;
++ if (enable) {
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, 0);
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, 1);
++ } else {
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, 0);
++ }
++
++ mdelay(100);
++}
++
++static void ov13850_reset(struct ov13850_dev *sensor)
++{
++ if (!sensor->reset_gpio)
++ return;
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++ mdelay(100);
++}
++
++static int ov13850_set_power_on(struct ov13850_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret;
++
++ ret = clk_prepare_enable(sensor->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ return ret;
++ }
++
++ ret = regulator_bulk_enable(OV13850_NUM_SUPPLIES,
++ sensor->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ goto xclk_off;
++ }
++
++ ov13850_reset(sensor);
++ ov13850_power(sensor, true);
++
++ return 0;
++
++xclk_off:
++ clk_disable_unprepare(sensor->xclk);
++ return ret;
++}
++
++static void ov13850_set_power_off(struct ov13850_dev *sensor)
++{
++ ov13850_power(sensor, false);
++ regulator_bulk_disable(OV13850_NUM_SUPPLIES, sensor->supplies);
++ clk_disable_unprepare(sensor->xclk);
++}
++
++static int ov13850_set_power_mipi(struct ov13850_dev *sensor, bool on)
++{
++ return 0;
++}
++
++static int ov13850_set_power(struct ov13850_dev *sensor, bool on)
++{
++ int ret = 0;
++ u16 chip_id;
++
++ if (on) {
++ ret = ov13850_set_power_on(sensor);
++ if (ret)
++ return ret;
++
++#ifdef UNUSED_CODE
++ ret = ov13850_read_reg16(sensor, OV13850_REG_CHIP_ID, &chip_id);
++ if (ret) {
++ dev_err(&sensor->i2c_client->dev, "%s: failed to read chip identifier\n",
++ __func__);
++ ret = -ENODEV;
++ goto power_off;
++ }
++
++ if (chip_id != OV13850_CHIP_ID) {
++ dev_err(&sensor->i2c_client->dev,
++ "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++ __func__, OV13850_CHIP_ID, chip_id);
++ ret = -ENXIO;
++ goto power_off;
++ }
++ dev_err(&sensor->i2c_client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, chip_id);
++#endif
++
++ ret = ov13850_restore_mode(sensor);
++ if (ret)
++ goto power_off;
++ }
++
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++ ret = ov13850_set_power_mipi(sensor, on);
++ if (ret)
++ goto power_off;
++
++ if (!on)
++ ov13850_set_power_off(sensor);
++
++ return 0;
++
++power_off:
++ ov13850_set_power_off(sensor);
++ return ret;
++}
++
++static int ov13850_s_power(struct v4l2_subdev *sd, int on)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ int ret = 0;
++
++ mutex_lock(&sensor->lock);
++
++ /*
++ * If the power count is modified from 0 to != 0 or from != 0 to 0,
++ * update the power state.
++ */
++ if (sensor->power_count == !on) {
++ ret = ov13850_set_power(sensor, !!on);
++ if (ret)
++ goto out;
++ }
++
++ /* Update the power count. */
++ sensor->power_count += on ? 1 : -1;
++ WARN_ON(sensor->power_count < 0);
++out:
++ mutex_unlock(&sensor->lock);
++
++ if (on && !ret && sensor->power_count == 1) {
++ /* restore controls */
++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++ }
++
++ return ret;
++}
++
++static int ov13850_try_frame_interval(struct ov13850_dev *sensor,
++ struct v4l2_fract *fi,
++ u32 width, u32 height)
++{
++ const struct ov13850_mode_info *mode;
++ enum ov13850_frame_rate rate = OV13850_15_FPS;
++ int minfps, maxfps, best_fps, fps;
++ int i;
++
++ minfps = ov13850_framerates[OV13850_15_FPS];
++ maxfps = ov13850_framerates[OV13850_NUM_FRAMERATES - 1];
++
++ if (fi->numerator == 0) {
++ fi->denominator = maxfps;
++ fi->numerator = 1;
++ rate = OV13850_60_FPS;
++ goto find_mode;
++ }
++
++ fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++ minfps, maxfps);
++
++ best_fps = minfps;
++ for (i = 0; i < ARRAY_SIZE(ov13850_framerates); i++) {
++ int curr_fps = ov13850_framerates[i];
++
++ if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++ best_fps = curr_fps;
++ rate = i;
++ }
++ }
++ st_info(ST_SENSOR, "best_fps = %d, fps = %d\n", best_fps, fps);
++
++ fi->numerator = 1;
++ fi->denominator = best_fps;
++
++find_mode:
++ mode = ov13850_find_mode(sensor, rate, width, height, false);
++ return mode ? rate : -EINVAL;
++}
++
++static int ov13850_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ if (code->pad != 0)
++ return -EINVAL;
++
++ if (code->index >= ARRAY_SIZE(ov13850_formats))
++ return -EINVAL;
++
++ code->code = ov13850_formats[code->index].code;
++ return 0;
++}
++
++static int ov13850_get_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ struct v4l2_mbus_framefmt *fmt;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++ format->pad);
++ else
++ fmt = &sensor->fmt;
++
++ format->format = *fmt;
++
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov13850_try_fmt_internal(struct v4l2_subdev *sd,
++ struct v4l2_mbus_framefmt *fmt,
++ enum ov13850_frame_rate fr,
++ const struct ov13850_mode_info **new_mode)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ const struct ov13850_mode_info *mode;
++ int i;
++
++ mode = ov13850_find_mode(sensor, fr, fmt->width, fmt->height, true);
++ if (!mode)
++ return -EINVAL;
++ fmt->width = mode->hact;
++ fmt->height = mode->vact;
++
++ if (new_mode)
++ *new_mode = mode;
++
++ for (i = 0; i < ARRAY_SIZE(ov13850_formats); i++)
++ if (ov13850_formats[i].code == fmt->code)
++ break;
++ if (i >= ARRAY_SIZE(ov13850_formats))
++ i = 0;
++
++ fmt->code = ov13850_formats[i].code;
++ fmt->colorspace = ov13850_formats[i].colorspace;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++ return 0;
++}
++
++static int ov13850_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ const struct ov13850_mode_info *new_mode;
++ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++ struct v4l2_mbus_framefmt *fmt;
++ int ret;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ ret = ov13850_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++ if (ret)
++ goto out;
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(sd, state, 0);
++ else
++ fmt = &sensor->fmt;
++
++ if (mbus_fmt->code != sensor->fmt.code)
++ sensor->pending_fmt_change = true;
++
++ *fmt = *mbus_fmt;
++
++ if (new_mode != sensor->current_mode) {
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++
++ if (new_mode->max_fps < sensor->current_fr) {
++ sensor->current_fr = new_mode->max_fps;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator =
++ ov13850_framerates[sensor->current_fr];
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov13850_calc_pixel_rate(sensor));
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int ov13850_set_framefmt(struct ov13850_dev *sensor,
++ struct v4l2_mbus_framefmt *format)
++{
++ u8 fmt;
++
++ switch (format->code) {
++ /* Raw, BGBG... / GRGR... */
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ fmt = 0x0;
++ break;
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ fmt = 0x1;
++ case MEDIA_BUS_FMT_SBGGR12_1X12:
++ case MEDIA_BUS_FMT_SGBRG12_1X12:
++ case MEDIA_BUS_FMT_SGRBG12_1X12:
++ case MEDIA_BUS_FMT_SRGGB12_1X12:
++ fmt = 0x2;
++ default:
++ return -EINVAL;
++ }
++
++ return ov13850_mod_reg(sensor, OV13850_REG_MIPI_SC,
++ BIT(1) | BIT(0), fmt);
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov13850_set_ctrl_hue(struct ov13850_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov13850_set_ctrl_contrast(struct ov13850_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov13850_set_ctrl_saturation(struct ov13850_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov13850_set_ctrl_white_balance(struct ov13850_dev *sensor, int awb)
++{
++ struct ov13850_ctrls *ctrls = &sensor->ctrls;
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov13850_set_ctrl_exposure(struct ov13850_dev *sensor,
++ enum v4l2_exposure_auto_type auto_exposure)
++{
++ struct ov13850_ctrls *ctrls = &sensor->ctrls;
++ int ret = 0;
++
++ return ret;
++}
++
++static const s64 link_freq_menu_items[] = {
++ OV13850_LINK_FREQ_500MHZ
++};
++
++static const char * const test_pattern_menu[] = {
++ "Disabled",
++ "Color bars",
++ "Color bars w/ rolling bar",
++ "Color squares",
++ "Color squares w/ rolling bar",
++};
++
++static int ov13850_set_ctrl_test_pattern(struct ov13850_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int ov13850_set_ctrl_light_freq(struct ov13850_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int ov13850_set_ctrl_hflip(struct ov13850_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int ov13850_set_ctrl_vflip(struct ov13850_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int ov13850_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ int val;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ val = ov13850_get_gain(sensor);
++ break;
++ }
++
++ return 0;
++}
++
++static int ov13850_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ int ret;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored right after power-up.
++ */
++ if (sensor->power_count == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = ov13850_set_gain(sensor, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = ov13850_set_ctrl_exposure(sensor, V4L2_EXPOSURE_MANUAL);
++ break;
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = ov13850_set_ctrl_white_balance(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HUE:
++ ret = ov13850_set_ctrl_hue(sensor, ctrl->val);
++ break;
++ case V4L2_CID_CONTRAST:
++ ret = ov13850_set_ctrl_contrast(sensor, ctrl->val);
++ break;
++ case V4L2_CID_SATURATION:
++ ret = ov13850_set_ctrl_saturation(sensor, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = ov13850_set_ctrl_test_pattern(sensor, ctrl->val);
++ break;
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ ret = ov13850_set_ctrl_light_freq(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = ov13850_set_ctrl_hflip(sensor, ctrl->val);
++ break;
++ case V4L2_CID_VFLIP:
++ ret = ov13850_set_ctrl_vflip(sensor, ctrl->val);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops ov13850_ctrl_ops = {
++ .g_volatile_ctrl = ov13850_g_volatile_ctrl,
++ .s_ctrl = ov13850_s_ctrl,
++};
++
++static int ov13850_init_controls(struct ov13850_dev *sensor)
++{
++ const struct v4l2_ctrl_ops *ops = &ov13850_ctrl_ops;
++ struct ov13850_ctrls *ctrls = &sensor->ctrls;
++ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++ int ret;
++
++ v4l2_ctrl_handler_init(hdl, 32);
++
++ /* we can use our own mutex for the ctrl lock */
++ hdl->lock = &sensor->lock;
++
++ /* Clock related controls */
++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++ 0, INT_MAX, 1,
++ ov13850_calc_pixel_rate(sensor));
++
++ /* Auto/manual white balance */
++ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, 1, 1, 0);
++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++ 0, 4095, 1, 1024);
++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++ 0, 4095, 1, 1024);
++
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 4, 0xfff8, 1, 0x4c00);
++ ctrls->anal_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
++ 0x10, 0xfff8, 1, 0x0080);
++ ctrls->test_pattern =
++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(test_pattern_menu) - 1,
++ 0, 0, test_pattern_menu);
++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++ 0, 1, 1, 0);
++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++ 0, 1, 1, 0);
++ ctrls->light_freq =
++ v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_POWER_LINE_FREQUENCY,
++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
++ 0, 0, link_freq_menu_items);
++ if (hdl->error) {
++ ret = hdl->error;
++ goto free_ctrls;
++ }
++
++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ // ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++ // ctrls->anal_gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++
++ sensor->sd.ctrl_handler = hdl;
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(hdl);
++ return ret;
++}
++
++static int ov13850_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ if (fse->pad != 0)
++ return -EINVAL;
++ if (fse->index >= OV13850_NUM_MODES)
++ return -EINVAL;
++
++ fse->min_width =
++ ov13850_mode_data[fse->index].hact;
++ fse->max_width = fse->min_width;
++ fse->min_height =
++ ov13850_mode_data[fse->index].vact;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int ov13850_enum_frame_interval(
++ struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_interval_enum *fie)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ struct v4l2_fract tpf;
++ int ret;
++
++ if (fie->pad != 0)
++ return -EINVAL;
++ if (fie->index >= OV13850_NUM_FRAMERATES)
++ return -EINVAL;
++
++ tpf.numerator = 1;
++ tpf.denominator = ov13850_framerates[fie->index];
++
++/* ret = ov13850_try_frame_interval(sensor, &tpf,
++ * fie->width, fie->height);
++ * if (ret < 0)
++ * return -EINVAL;
++ */
++ fie->interval = tpf;
++
++ return 0;
++}
++
++static int ov13850_g_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++
++ mutex_lock(&sensor->lock);
++ fi->interval = sensor->frame_interval;
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov13850_s_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ const struct ov13850_mode_info *mode;
++ int frame_rate, ret = 0;
++
++ if (fi->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ mode = sensor->current_mode;
++
++ frame_rate = ov13850_try_frame_interval(sensor, &fi->interval,
++ mode->hact, mode->vact);
++ if (frame_rate < 0) {
++ /* Always return a valid frame interval value */
++ fi->interval = sensor->frame_interval;
++ goto out;
++ }
++
++ mode = ov13850_find_mode(sensor, frame_rate, mode->hact,
++ mode->vact, true);
++ if (!mode) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (mode != sensor->current_mode ||
++ frame_rate != sensor->current_fr) {
++ sensor->current_fr = frame_rate;
++ sensor->frame_interval = fi->interval;
++ sensor->current_mode = mode;
++ sensor->pending_mode_change = true;
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov13850_calc_pixel_rate(sensor));
++ }
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int ov13850_stream_start(struct ov13850_dev *sensor, int enable)
++{
++ int ret;
++
++ if (enable) { //stream on
++ mdelay(1000);
++ ret = ov13850_write_reg(sensor, OV13850_STREAM_CTRL, enable);
++ } else { //stream off
++ ret = ov13850_write_reg(sensor, OV13850_STREAM_CTRL, enable);
++ mdelay(100);
++ }
++
++ return ret;
++}
++
++static int ov13850_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++ int ret = 0;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming == !enable) {
++ if (enable && sensor->pending_mode_change) {
++ ret = ov13850_set_mode(sensor);
++ if (ret)
++ goto out;
++ }
++
++ if (enable && sensor->pending_fmt_change) {
++ ret = ov13850_set_framefmt(sensor, &sensor->fmt);
++ if (ret)
++ goto out;
++ sensor->pending_fmt_change = false;
++ }
++
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++ ret = ov13850_set_stream_mipi(sensor, enable);
++
++ ret = ov13850_stream_start(sensor, enable);
++
++ if (!ret)
++ sensor->streaming = enable;
++ }
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static const struct v4l2_subdev_core_ops ov13850_core_ops = {
++ .s_power = ov13850_s_power,
++ .log_status = v4l2_ctrl_subdev_log_status,
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov13850_video_ops = {
++ .g_frame_interval = ov13850_g_frame_interval,
++ .s_frame_interval = ov13850_s_frame_interval,
++ .s_stream = ov13850_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov13850_pad_ops = {
++ .enum_mbus_code = ov13850_enum_mbus_code,
++ .get_fmt = ov13850_get_fmt,
++ .set_fmt = ov13850_set_fmt,
++ .enum_frame_size = ov13850_enum_frame_size,
++ .enum_frame_interval = ov13850_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops ov13850_subdev_ops = {
++ .core = &ov13850_core_ops,
++ .video = &ov13850_video_ops,
++ .pad = &ov13850_pad_ops,
++};
++
++static int ov13850_get_regulators(struct ov13850_dev *sensor)
++{
++ int i;
++
++ for (i = 0; i < OV13850_NUM_SUPPLIES; i++)
++ sensor->supplies[i].supply = ov13850_supply_name[i];
++
++ return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++ OV13850_NUM_SUPPLIES,
++ sensor->supplies);
++}
++
++static int ov13850_check_chip_id(struct ov13850_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret = 0;
++ u16 chip_id;
++
++ ret = ov13850_set_power_on(sensor);
++ if (ret)
++ return ret;
++
++#ifdef UNUSED_CODE
++ ret = ov13850_read_reg16(sensor, OV13850_REG_CHIP_ID, &chip_id);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to read chip identifier\n",
++ __func__);
++ goto power_off;
++ }
++
++ if (chip_id != OV13850_CHIP_ID) {
++ dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++ __func__, OV13850_CHIP_ID, chip_id);
++ ret = -ENXIO;
++ }
++ dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, chip_id);
++#endif
++
++power_off:
++ ov13850_set_power_off(sensor);
++ return ret;
++}
++
++static int ov13850_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct fwnode_handle *endpoint;
++ struct ov13850_dev *sensor;
++ struct v4l2_mbus_framefmt *fmt;
++ u32 rotation;
++ int ret;
++ u8 chip_id_high, chip_id_low;
++
++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++ if (!sensor)
++ return -ENOMEM;
++
++ sensor->i2c_client = client;
++
++ fmt = &sensor->fmt;
++ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = 1920;
++ fmt->height = 1080;
++ fmt->field = V4L2_FIELD_NONE;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator = ov13850_framerates[OV13850_30_FPS];
++ sensor->current_fr = OV13850_30_FPS;
++ sensor->current_mode =
++ &ov13850_mode_data[OV13850_MODE_1080P_1920_1080];
++ sensor->last_mode = sensor->current_mode;
++
++ sensor->ae_target = 52;
++
++ /* optional indication of physical rotation of sensor */
++ ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++ &rotation);
++ if (!ret) {
++ switch (rotation) {
++ case 180:
++ sensor->upside_down = true;
++ fallthrough;
++ case 0:
++ break;
++ default:
++ dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++ rotation);
++ }
++ }
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++ NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++ fwnode_handle_put(endpoint);
++ if (ret) {
++ dev_err(dev, "Could not parse endpoint\n");
++ return ret;
++ }
++
++ if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
++ sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
++ sensor->ep.bus_type != V4L2_MBUS_BT656) {
++ dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++ return -EINVAL;
++ }
++
++ /* get system clock (xclk) */
++ sensor->xclk = devm_clk_get(dev, "xclk");
++ if (IS_ERR(sensor->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(sensor->xclk);
++ }
++
++ sensor->xclk_freq = clk_get_rate(sensor->xclk);
++ if (sensor->xclk_freq < OV13850_XCLK_MIN ||
++ sensor->xclk_freq > OV13850_XCLK_MAX) {
++ dev_err(dev, "xclk frequency out of range: %d Hz\n",
++ sensor->xclk_freq);
++ return -EINVAL;
++ }
++
++ /* request optional power down pin */
++ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->pwdn_gpio))
++ return PTR_ERR(sensor->pwdn_gpio);
++
++ /* request optional reset pin */
++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->reset_gpio))
++ return PTR_ERR(sensor->reset_gpio);
++
++ v4l2_i2c_subdev_init(&sensor->sd, client, &ov13850_subdev_ops);
++
++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++ if (ret)
++ return ret;
++
++ ret = ov13850_get_regulators(sensor);
++ if (ret)
++ return ret;
++
++ mutex_init(&sensor->lock);
++
++ ret = ov13850_check_chip_id(sensor);
++ if (ret)
++ goto entity_cleanup;
++
++ ret = ov13850_init_controls(sensor);
++ if (ret)
++ goto entity_cleanup;
++
++ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++ if (ret)
++ goto free_ctrls;
++
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_cleanup:
++ media_entity_cleanup(&sensor->sd.entity);
++ mutex_destroy(&sensor->lock);
++ return ret;
++}
++
++static int ov13850_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov13850_dev *sensor = to_ov13850_dev(sd);
++
++ v4l2_async_unregister_subdev(&sensor->sd);
++ media_entity_cleanup(&sensor->sd.entity);
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++ mutex_destroy(&sensor->lock);
++
++ return 0;
++}
++
++static const struct i2c_device_id ov13850_id[] = {
++ {"ov13850", 0},
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, ov13850_id);
++
++static const struct of_device_id ov13850_dt_ids[] = {
++ { .compatible = "ovti,ov13850" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov13850_dt_ids);
++
++static struct i2c_driver ov13850_i2c_driver = {
++ .driver = {
++ .name = "ov13850",
++ .of_match_table = ov13850_dt_ids,
++ },
++ .id_table = ov13850_id,
++ .probe_new = ov13850_probe,
++ .remove = ov13850_remove,
++};
++
++module_i2c_driver(ov13850_i2c_driver);
++
++MODULE_DESCRIPTION("OV13850 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
+@@ -0,0 +1,2975 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++
++#define OV4689_LANES 4
++
++#define OV4689_LINK_FREQ_500MHZ 500000000LL
++
++/* min/typical/max system clock (xclk) frequencies */
++#define OV4689_XCLK_MIN 6000000
++#define OV4689_XCLK_MAX 64000000
++
++#define OV4689_CHIP_ID (0x4688)
++
++#define OV4689_CHIP_ID_HIGH_BYTE 0x300a // max should be 0x46
++#define OV4689_CHIP_ID_LOW_BYTE 0x300b // max should be 0x88
++#define OV4689_REG_CHIP_ID 0x300a
++
++#define OV4689_REG_H_OUTPUT_SIZE 0x3808
++#define OV4689_REG_V_OUTPUT_SIZE 0x380a
++#define OV4689_REG_TIMING_HTS 0x380c
++#define OV4689_REG_TIMING_VTS 0x380e
++
++#define OV4689_REG_EXPOSURE_HI 0x3500
++#define OV4689_REG_EXPOSURE_MED 0x3501
++#define OV4689_REG_EXPOSURE_LO 0x3502
++#define OV4689_REG_GAIN_H 0x3507
++#define OV4689_REG_GAIN_M 0x3508
++#define OV4689_REG_GAIN_L 0x3509
++#define OV4689_REG_TEST_PATTERN 0x5040
++#define OV4689_REG_TIMING_TC_REG20 0x3820
++#define OV4689_REG_TIMING_TC_REG21 0x3821
++
++#define OV4689_REG_AWB_R_GAIN 0x500C
++#define OV4689_REG_AWB_B_GAIN 0x5010
++#define OV4689_REG_STREAM_ON 0x0100
++
++#define OV4689_REG_MIPI_SC_CTRL_HI 0x3018
++#define OV4689_REG_MIPI_SC_CTRL_LOW 0x3019
++
++enum ov4689_mode_id {
++ //OV4689_MODE_720P_1280_720 = 0,
++ OV4689_MODE_1080P_1920_1080 = 0,
++ //OV4689_MODE_4M_2688_1520,
++ OV4689_NUM_MODES,
++};
++
++enum ov4689_frame_rate {
++ OV4689_15_FPS = 0,
++ OV4689_30_FPS,
++ OV4689_45_FPS,
++ OV4689_60_FPS,
++ OV4689_90_FPS,
++ OV4689_120_FPS,
++ OV4689_150_FPS,
++ OV4689_180_FPS,
++ OV4689_330_FPS,
++ OV4689_NUM_FRAMERATES,
++};
++
++enum ov4689_format_mux {
++ OV4689_FMT_MUX_RAW,
++};
++
++static const int ov4689_framerates[] = {
++ [OV4689_15_FPS] = 15,
++ [OV4689_30_FPS] = 30,
++ [OV4689_45_FPS] = 45,
++ [OV4689_60_FPS] = 60,
++ [OV4689_90_FPS] = 90,
++ [OV4689_120_FPS] = 120,
++ [OV4689_150_FPS] = 150,
++ [OV4689_180_FPS] = 180,
++ [OV4689_330_FPS] = 330,
++};
++
++/* regulator supplies */
++static const char * const ov4689_supply_name[] = {
++ "DOVDD", /* Digital I/O (1.8V) supply */
++ "AVDD", /* Analog (2.8V) supply */
++ "DVDD", /* Digital Core (1.5V) supply */
++};
++
++#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov4689_downsize_mode {
++ SUBSAMPLING,
++ SCALING,
++};
++
++struct reg_value {
++ u16 reg_addr;
++ u8 val;
++ u8 mask;
++ u32 delay_ms;
++};
++
++struct ov4689_mode_info {
++ enum ov4689_mode_id id;
++ enum ov4689_downsize_mode dn_mode;
++ u32 hact;
++ u32 htot;
++ u32 vact;
++ u32 vtot;
++ const struct reg_value *reg_data;
++ u32 reg_data_size;
++ u32 max_fps;
++};
++
++struct ov4689_ctrls {
++ struct v4l2_ctrl_handler handler;
++ struct v4l2_ctrl *pixel_rate;
++ struct {
++ struct v4l2_ctrl *exposure;
++ };
++ struct {
++ struct v4l2_ctrl *auto_wb;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *red_balance;
++ };
++ struct {
++ struct v4l2_ctrl *anal_gain;
++ };
++ struct v4l2_ctrl *brightness;
++ struct v4l2_ctrl *light_freq;
++ struct v4l2_ctrl *link_freq;
++ struct v4l2_ctrl *saturation;
++ struct v4l2_ctrl *contrast;
++ struct v4l2_ctrl *hue;
++ struct v4l2_ctrl *test_pattern;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vflip;
++};
++
++struct ov4689_dev {
++ struct i2c_client *i2c_client;
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++ struct clk *xclk; /* system clock to OV4689 */
++ u32 xclk_freq;
++
++ struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
++ struct gpio_desc *reset_gpio;
++ struct gpio_desc *pwdn_gpio;
++ bool upside_down;
++
++ /* lock to protect all members below */
++ struct mutex lock;
++
++ struct v4l2_mbus_framefmt fmt;
++
++ const struct ov4689_mode_info *current_mode;
++ const struct ov4689_mode_info *last_mode;
++ enum ov4689_frame_rate current_fr;
++ struct v4l2_fract frame_interval;
++
++ struct ov4689_ctrls ctrls;
++
++ bool pending_mode_change;
++ int streaming;
++};
++
++static inline struct ov4689_dev *to_ov4689_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ov4689_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct ov4689_dev,
++ ctrls.handler)->sd;
++}
++
++/* ov4689 initial register */
++static const struct reg_value ov4689_init_setting_30fps_1080P[] = {
++/* ov4689_1080p_30fps_4d */
++ {0x0103, 0x01, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x0300, 0x02, 0, 0},
++ {0x0302, 0x32, 0, 0},
++ {0x0303, 0x00, 0, 0},
++ {0x0304, 0x03, 0, 0},
++ {0x030b, 0x00, 0, 0},
++ {0x030d, 0x1e, 0, 0},
++ {0x030e, 0x04, 0, 0},
++ {0x030f, 0x01, 0, 0},
++ {0x0312, 0x01, 0, 0},
++ {0x031e, 0x00, 0, 0},
++ {0x3000, 0x20, 0, 0},
++ {0x3002, 0x00, 0, 0},
++ {0x3020, 0x93, 0, 0},
++ {0x3021, 0x03, 0, 0},
++ {0x3022, 0x01, 0, 0},
++ {0x3031, 0x0a, 0, 0},
++ {0x3305, 0xf1, 0, 0},
++ {0x3307, 0x04, 0, 0},
++ {0x3309, 0x29, 0, 0},
++ {0x3500, 0x00, 0, 0},
++ {0x3501, 0x4c, 0, 0},
++ {0x3502, 0x00, 0, 0},
++ {0x3503, 0x04, 0, 0},
++ {0x3504, 0x00, 0, 0},
++ {0x3505, 0x00, 0, 0},
++ {0x3506, 0x00, 0, 0},
++ {0x3507, 0x00, 0, 0},
++ {0x3508, 0x00, 0, 0},
++ {0x3509, 0x80, 0, 0},
++ {0x350a, 0x00, 0, 0},
++ {0x350b, 0x00, 0, 0},
++ {0x350c, 0x00, 0, 0},
++ {0x350d, 0x00, 0, 0},
++ {0x350e, 0x00, 0, 0},
++ {0x350f, 0x80, 0, 0},
++ {0x3510, 0x00, 0, 0},
++ {0x3511, 0x00, 0, 0},
++ {0x3512, 0x00, 0, 0},
++ {0x3513, 0x00, 0, 0},
++ {0x3514, 0x00, 0, 0},
++ {0x3515, 0x80, 0, 0},
++ {0x3516, 0x00, 0, 0},
++ {0x3517, 0x00, 0, 0},
++ {0x3518, 0x00, 0, 0},
++ {0x3519, 0x00, 0, 0},
++ {0x351a, 0x00, 0, 0},
++ {0x351b, 0x80, 0, 0},
++ {0x351c, 0x00, 0, 0},
++ {0x351d, 0x00, 0, 0},
++ {0x351e, 0x00, 0, 0},
++ {0x351f, 0x00, 0, 0},
++ {0x3520, 0x00, 0, 0},
++ {0x3521, 0x80, 0, 0},
++ {0x3522, 0x08, 0, 0},
++ {0x3524, 0x08, 0, 0},
++ {0x3526, 0x08, 0, 0},
++ {0x3528, 0x08, 0, 0},
++ {0x352a, 0x08, 0, 0},
++ {0x3602, 0x00, 0, 0},
++ {0x3603, 0x40, 0, 0},
++ {0x3604, 0x02, 0, 0},
++ {0x3605, 0x00, 0, 0},
++ {0x3606, 0x00, 0, 0},
++ {0x3607, 0x00, 0, 0},
++ {0x3609, 0x12, 0, 0},
++ {0x360a, 0x40, 0, 0},
++ {0x360c, 0x08, 0, 0},
++ {0x360f, 0xe5, 0, 0},
++ {0x3608, 0x8f, 0, 0},
++ {0x3611, 0x00, 0, 0},
++ {0x3613, 0xf7, 0, 0},
++ {0x3616, 0x58, 0, 0},
++ {0x3619, 0x99, 0, 0},
++ {0x361b, 0x60, 0, 0},
++ {0x361c, 0x7a, 0, 0},
++ {0x361e, 0x79, 0, 0},
++ {0x361f, 0x02, 0, 0},
++ {0x3632, 0x00, 0, 0},
++ {0x3633, 0x10, 0, 0},
++ {0x3634, 0x10, 0, 0},
++ {0x3635, 0x10, 0, 0},
++ {0x3636, 0x15, 0, 0},
++ {0x3646, 0x86, 0, 0},
++ {0x364a, 0x0b, 0, 0},
++ {0x3700, 0x17, 0, 0},
++ {0x3701, 0x22, 0, 0},
++ {0x3703, 0x10, 0, 0},
++ {0x370a, 0x37, 0, 0},
++ {0x3705, 0x00, 0, 0},
++ {0x3706, 0x63, 0, 0},
++ {0x3709, 0x3c, 0, 0},
++ {0x370b, 0x01, 0, 0},
++ {0x370c, 0x30, 0, 0},
++ {0x3710, 0x24, 0, 0},
++ {0x3711, 0x0c, 0, 0},
++ {0x3716, 0x00, 0, 0},
++ {0x3720, 0x28, 0, 0},
++ {0x3729, 0x7b, 0, 0},
++ {0x372a, 0x84, 0, 0},
++ {0x372b, 0xbd, 0, 0},
++ {0x372c, 0xbc, 0, 0},
++ {0x372e, 0x52, 0, 0},
++ {0x373c, 0x0e, 0, 0},
++ {0x373e, 0x33, 0, 0},
++ {0x3743, 0x10, 0, 0},
++ {0x3744, 0x88, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x374a, 0x43, 0, 0},
++ {0x374c, 0x00, 0, 0},
++ {0x374e, 0x23, 0, 0},
++ {0x3751, 0x7b, 0, 0},
++ {0x3752, 0x84, 0, 0},
++ {0x3753, 0xbd, 0, 0},
++ {0x3754, 0xbc, 0, 0},
++ {0x3756, 0x52, 0, 0},
++ {0x375c, 0x00, 0, 0},
++ {0x3760, 0x00, 0, 0},
++ {0x3761, 0x00, 0, 0},
++ {0x3762, 0x00, 0, 0},
++ {0x3763, 0x00, 0, 0},
++ {0x3764, 0x00, 0, 0},
++ {0x3767, 0x04, 0, 0},
++ {0x3768, 0x04, 0, 0},
++ {0x3769, 0x08, 0, 0},
++ {0x376a, 0x08, 0, 0},
++ {0x376b, 0x20, 0, 0},
++ {0x376c, 0x00, 0, 0},
++ {0x376d, 0x00, 0, 0},
++ {0x376e, 0x00, 0, 0},
++ {0x3773, 0x00, 0, 0},
++ {0x3774, 0x51, 0, 0},
++ {0x3776, 0xbd, 0, 0},
++ {0x3777, 0xbd, 0, 0},
++ {0x3781, 0x18, 0, 0},
++ {0x3783, 0x25, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++ {0x3800, 0x01, 0, 0},
++ {0x3801, 0x88, 0, 0},
++ {0x3802, 0x00, 0, 0},
++ {0x3803, 0xe0, 0, 0},
++ {0x3804, 0x09, 0, 0},
++ {0x3805, 0x17, 0, 0},
++ {0x3806, 0x05, 0, 0},
++ {0x3807, 0x1f, 0, 0},
++ {0x3808, 0x07, 0, 0},
++ {0x3809, 0x80, 0, 0},
++ {0x380a, 0x04, 0, 0},
++ {0x380b, 0x38, 0, 0},
++ {0x380c, 0x0d, 0, 0},
++ {0x380d, 0x70, 0, 0},
++ {0x380e, 0x04, 0, 0},
++ {0x380f, 0x8A, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x08, 0, 0},
++ {0x3812, 0x00, 0, 0},
++ {0x3813, 0x04, 0, 0},
++ {0x3814, 0x01, 0, 0},
++ {0x3815, 0x01, 0, 0},
++ {0x3819, 0x01, 0, 0},
++ {0x3820, 0x06, 0, 0},
++ {0x3821, 0x00, 0, 0},
++ {0x3829, 0x00, 0, 0},
++ {0x382a, 0x01, 0, 0},
++ {0x382b, 0x01, 0, 0},
++ {0x382d, 0x7f, 0, 0},
++ {0x3830, 0x04, 0, 0},
++ {0x3836, 0x01, 0, 0},
++ {0x3837, 0x00, 0, 0},
++ {0x3841, 0x02, 0, 0},
++ {0x3846, 0x08, 0, 0},
++ {0x3847, 0x07, 0, 0},
++ {0x3d85, 0x36, 0, 0},
++ {0x3d8c, 0x71, 0, 0},
++ {0x3d8d, 0xcb, 0, 0},
++ {0x3f0a, 0x00, 0, 0},
++ {0x4000, 0xf1, 0, 0},
++ {0x4001, 0x40, 0, 0},
++ {0x4002, 0x04, 0, 0},
++ {0x4003, 0x14, 0, 0},
++ {0x400e, 0x00, 0, 0},
++ {0x4011, 0x00, 0, 0},
++ {0x401a, 0x00, 0, 0},
++ {0x401b, 0x00, 0, 0},
++ {0x401c, 0x00, 0, 0},
++ {0x401d, 0x00, 0, 0},
++ {0x401f, 0x00, 0, 0},
++ {0x4020, 0x00, 0, 0},
++ {0x4021, 0x10, 0, 0},
++ {0x4022, 0x06, 0, 0},
++ {0x4023, 0x13, 0, 0},
++ {0x4024, 0x07, 0, 0},
++ {0x4025, 0x40, 0, 0},
++ {0x4026, 0x07, 0, 0},
++ {0x4027, 0x50, 0, 0},
++ {0x4028, 0x00, 0, 0},
++ {0x4029, 0x02, 0, 0},
++ {0x402a, 0x06, 0, 0},
++ {0x402b, 0x04, 0, 0},
++ {0x402c, 0x02, 0, 0},
++ {0x402d, 0x02, 0, 0},
++ {0x402e, 0x0e, 0, 0},
++ {0x402f, 0x04, 0, 0},
++ {0x4302, 0xff, 0, 0},
++ {0x4303, 0xff, 0, 0},
++ {0x4304, 0x00, 0, 0},
++ {0x4305, 0x00, 0, 0},
++ {0x4306, 0x00, 0, 0},
++ {0x4308, 0x02, 0, 0},
++ {0x4500, 0x6c, 0, 0},
++ {0x4501, 0xc4, 0, 0},
++ {0x4502, 0x40, 0, 0},
++ {0x4503, 0x01, 0, 0},
++ {0x4601, 0x77, 0, 0},
++ {0x4800, 0x04, 0, 0},
++ {0x4813, 0x08, 0, 0},
++ {0x481f, 0x40, 0, 0},
++ {0x4829, 0x78, 0, 0},
++ {0x4837, 0x10, 0, 0},
++ {0x4b00, 0x2a, 0, 0},
++ {0x4b0d, 0x00, 0, 0},
++ {0x4d00, 0x04, 0, 0},
++ {0x4d01, 0x42, 0, 0},
++ {0x4d02, 0xd1, 0, 0},
++ {0x4d03, 0x93, 0, 0},
++ {0x4d04, 0xf5, 0, 0},
++ {0x4d05, 0xc1, 0, 0},
++ {0x5000, 0xf3, 0, 0},
++ {0x5001, 0x11, 0, 0},
++ {0x5004, 0x00, 0, 0},
++ {0x500a, 0x00, 0, 0},
++ {0x500b, 0x00, 0, 0},
++ {0x5032, 0x00, 0, 0},
++ {0x5040, 0x00, 0, 0},
++ {0x5050, 0x0c, 0, 0},
++ {0x5500, 0x00, 0, 0},
++ {0x5501, 0x10, 0, 0},
++ {0x5502, 0x01, 0, 0},
++ {0x5503, 0x0f, 0, 0},
++ {0x8000, 0x00, 0, 0},
++ {0x8001, 0x00, 0, 0},
++ {0x8002, 0x00, 0, 0},
++ {0x8003, 0x00, 0, 0},
++ {0x8004, 0x00, 0, 0},
++ {0x8005, 0x00, 0, 0},
++ {0x8006, 0x00, 0, 0},
++ {0x8007, 0x00, 0, 0},
++ {0x8008, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_VGA_640_480[] = {
++ //@@ RES_640x480_2x_Bin_330fps_816Mbps
++ //OV4689_AM01B_640x480_24M_2lane_816Mbps_330fps_20140210.txt
++ {0x0103, 0x01, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x0300, 0x00, 0, 0}, // 00
++ {0x0302, 0x22, 0, 0}, // 816Mbps 5a ; 64 ; 5a ; 78 ; 78 ; 2a
++ {0x0303, 0x00, 0, 0}, // 03 ; 01 ; 02 ;
++ {0x0304, 0x03, 0, 0},
++ {0x030b, 0x00, 0, 0},
++ {0x030d, 0x1e, 0, 0},
++ {0x030e, 0x04, 0, 0},
++ {0x030f, 0x01, 0, 0},
++ {0x0312, 0x01, 0, 0},
++ {0x031e, 0x00, 0, 0},
++ {0x3000, 0x20, 0, 0},
++ {0x3002, 0x00, 0, 0},
++ {0x3020, 0x93, 0, 0},
++ {0x3021, 0x03, 0, 0},
++ {0x3022, 0x01, 0, 0},
++ {0x3031, 0x0a, 0, 0},
++ {0x303f, 0x0c, 0, 0},
++ {0x3305, 0xf1, 0, 0},
++ {0x3307, 0x04, 0, 0},
++ {0x3309, 0x29, 0, 0},
++ {0x3500, 0x00, 0, 0},
++ {0x3501, 0x4c, 0, 0},
++ {0x3502, 0x00, 0, 0},
++ {0x3503, 0x04, 0, 0},
++ {0x3504, 0x00, 0, 0},
++ {0x3505, 0x00, 0, 0},
++ {0x3506, 0x00, 0, 0},
++ {0x3507, 0x00, 0, 0},
++ {0x3508, 0x00, 0, 0},
++ {0x3509, 0x80, 0, 0}, // 8X
++ {0x350a, 0x00, 0, 0},
++ {0x350b, 0x00, 0, 0},
++ {0x350c, 0x00, 0, 0},
++ {0x350d, 0x00, 0, 0},
++ {0x350e, 0x00, 0, 0},
++ {0x350f, 0x80, 0, 0},
++ {0x3510, 0x00, 0, 0},
++ {0x3511, 0x00, 0, 0},
++ {0x3512, 0x00, 0, 0},
++ {0x3513, 0x00, 0, 0},
++ {0x3514, 0x00, 0, 0},
++ {0x3515, 0x80, 0, 0},
++ {0x3516, 0x00, 0, 0},
++ {0x3517, 0x00, 0, 0},
++ {0x3518, 0x00, 0, 0},
++ {0x3519, 0x00, 0, 0},
++ {0x351a, 0x00, 0, 0},
++ {0x351b, 0x80, 0, 0},
++ {0x351c, 0x00, 0, 0},
++ {0x351d, 0x00, 0, 0},
++ {0x351e, 0x00, 0, 0},
++ {0x351f, 0x00, 0, 0},
++ {0x3520, 0x00, 0, 0},
++ {0x3521, 0x80, 0, 0},
++ {0x3522, 0x08, 0, 0},
++ {0x3524, 0x08, 0, 0},
++ {0x3526, 0x08, 0, 0},
++ {0x3528, 0x08, 0, 0},
++ {0x352a, 0x08, 0, 0},
++ {0x3602, 0x00, 0, 0},
++ {0x3603, 0x40, 0, 0},
++ {0x3604, 0x02, 0, 0},
++ {0x3605, 0x00, 0, 0},
++ {0x3606, 0x00, 0, 0},
++ {0x3607, 0x00, 0, 0},
++ {0x3609, 0x12, 0, 0},
++ {0x360a, 0x40, 0, 0},
++ {0x360c, 0x08, 0, 0},
++ {0x360f, 0xe5, 0, 0},
++ {0x3608, 0x8f, 0, 0},
++ {0x3611, 0x00, 0, 0},
++ {0x3613, 0xf7, 0, 0},
++ {0x3616, 0x58, 0, 0},
++ {0x3619, 0x99, 0, 0},
++ {0x361b, 0x60, 0, 0},
++ {0x361c, 0x7a, 0, 0},
++ {0x361e, 0x79, 0, 0},
++ {0x361f, 0x02, 0, 0},
++ {0x3632, 0x05, 0, 0},
++ {0x3633, 0x10, 0, 0},
++ {0x3634, 0x10, 0, 0},
++ {0x3635, 0x10, 0, 0},
++ {0x3636, 0x15, 0, 0},
++ {0x3646, 0x86, 0, 0},
++ {0x364a, 0x0b, 0, 0},
++ {0x3700, 0x17, 0, 0},
++ {0x3701, 0x22, 0, 0},
++ {0x3703, 0x10, 0, 0},
++ {0x370a, 0x37, 0, 0},
++ {0x3705, 0x00, 0, 0},
++ {0x3706, 0x63, 0, 0},
++ {0x3709, 0x3c, 0, 0},
++ {0x370b, 0x01, 0, 0},
++ {0x370c, 0x30, 0, 0},
++ {0x3710, 0x24, 0, 0},
++ {0x3711, 0x0c, 0, 0},
++ {0x3716, 0x00, 0, 0},
++ {0x3720, 0x28, 0, 0},
++ {0x3729, 0x7b, 0, 0},
++ {0x372a, 0x84, 0, 0},
++ {0x372b, 0xbd, 0, 0},
++ {0x372c, 0xbc, 0, 0},
++ {0x372e, 0x52, 0, 0},
++ {0x373c, 0x0e, 0, 0},
++ {0x373e, 0x33, 0, 0},
++ {0x3743, 0x10, 0, 0},
++ {0x3744, 0x88, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x374a, 0x43, 0, 0},
++ {0x374c, 0x00, 0, 0},
++ {0x374e, 0x23, 0, 0},
++ {0x3751, 0x7b, 0, 0},
++ {0x3752, 0x84, 0, 0},
++ {0x3753, 0xbd, 0, 0},
++ {0x3754, 0xbc, 0, 0},
++ {0x3756, 0x52, 0, 0},
++ {0x375c, 0x00, 0, 0},
++ {0x3760, 0x00, 0, 0},
++ {0x3761, 0x00, 0, 0},
++ {0x3762, 0x00, 0, 0},
++ {0x3763, 0x00, 0, 0},
++ {0x3764, 0x00, 0, 0},
++ {0x3767, 0x04, 0, 0},
++ {0x3768, 0x04, 0, 0},
++ {0x3769, 0x08, 0, 0},
++ {0x376a, 0x08, 0, 0},
++ {0x376b, 0x40, 0, 0},
++ {0x376c, 0x00, 0, 0},
++ {0x376d, 0x00, 0, 0},
++ {0x376e, 0x00, 0, 0},
++ {0x3773, 0x00, 0, 0},
++ {0x3774, 0x51, 0, 0},
++ {0x3776, 0xbd, 0, 0},
++ {0x3777, 0xbd, 0, 0},
++ {0x3781, 0x18, 0, 0},
++ {0x3783, 0x25, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++ {0x3800, 0x00, 0, 0},
++ {0x3801, 0x48, 0, 0},
++ {0x3802, 0x00, 0, 0},
++ {0x3803, 0x2C, 0, 0},
++ {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x57, 0, 0},
++ {0x3806, 0x05, 0, 0},
++ {0x3807, 0xD3, 0, 0},
++ {0x3808, 0x02, 0, 0},
++ {0x3809, 0x80, 0, 0},
++ {0x380a, 0x01, 0, 0},
++ {0x380b, 0xe0, 0, 0},
++
++ {0x380c, 0x02, 0, 0}, // 0a ; 03
++ {0x380d, 0x04, 0, 0}, // 1c ; 5C
++
++ {0x380e, 0x03, 0, 0},
++ {0x380f, 0x05, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x04, 0, 0},
++ {0x3812, 0x00, 0, 0},
++ {0x3813, 0x02, 0, 0},
++ {0x3814, 0x03, 0, 0},
++ {0x3815, 0x01, 0, 0},
++ {0x3819, 0x01, 0, 0},
++ {0x3820, 0x06, 0, 0},
++ {0x3821, 0x00, 0, 0},
++ {0x3829, 0x00, 0, 0},
++ {0x382a, 0x03, 0, 0},
++ {0x382b, 0x01, 0, 0},
++ {0x382d, 0x7f, 0, 0},
++ {0x3830, 0x08, 0, 0},
++ {0x3836, 0x02, 0, 0},
++ {0x3837, 0x00, 0, 0},
++ {0x3841, 0x02, 0, 0},
++ {0x3846, 0x08, 0, 0},
++ {0x3847, 0x07, 0, 0},
++ {0x3d85, 0x36, 0, 0},
++ {0x3d8c, 0x71, 0, 0},
++ {0x3d8d, 0xcb, 0, 0},
++ {0x3f0a, 0x00, 0, 0},
++ {0x4000, 0x71, 0, 0},
++ {0x4001, 0x50, 0, 0},
++ {0x4002, 0x04, 0, 0},
++ {0x4003, 0x14, 0, 0},
++ {0x400e, 0x00, 0, 0},
++ {0x4011, 0x00, 0, 0},
++ {0x401a, 0x00, 0, 0},
++ {0x401b, 0x00, 0, 0},
++ {0x401c, 0x00, 0, 0},
++ {0x401d, 0x00, 0, 0},
++ {0x401f, 0x00, 0, 0},
++ {0x4020, 0x00, 0, 0},
++ {0x4021, 0x10, 0, 0},
++ {0x4022, 0x03, 0, 0},
++ {0x4023, 0x93, 0, 0},
++ {0x4024, 0x04, 0, 0},
++ {0x4025, 0xC0, 0, 0},
++ {0x4026, 0x04, 0, 0},
++ {0x4027, 0xD0, 0, 0},
++ {0x4028, 0x00, 0, 0},
++ {0x4029, 0x02, 0, 0},
++ {0x402a, 0x06, 0, 0},
++ {0x402b, 0x04, 0, 0},
++ {0x402c, 0x02, 0, 0},
++ {0x402d, 0x02, 0, 0},
++ {0x402e, 0x0e, 0, 0},
++ {0x402f, 0x04, 0, 0},
++ {0x4302, 0xff, 0, 0},
++ {0x4303, 0xff, 0, 0},
++ {0x4304, 0x00, 0, 0},
++ {0x4305, 0x00, 0, 0},
++ {0x4306, 0x00, 0, 0},
++ {0x4308, 0x02, 0, 0},
++ {0x4500, 0x6c, 0, 0},
++ {0x4501, 0xc4, 0, 0},
++ {0x4502, 0x44, 0, 0},
++ {0x4503, 0x01, 0, 0},
++ {0x4600, 0x00, 0, 0},
++ {0x4601, 0x4F, 0, 0},
++ {0x4800, 0x04, 0, 0},
++ {0x4813, 0x08, 0, 0},
++ {0x481f, 0x40, 0, 0},
++ {0x4829, 0x78, 0, 0},
++ {0x4837, 0x10, 0, 0}, // 20 ; 10
++ {0x4b00, 0x2a, 0, 0},
++ {0x4b0d, 0x00, 0, 0},
++ {0x4d00, 0x04, 0, 0},
++ {0x4d01, 0x42, 0, 0},
++ {0x4d02, 0xd1, 0, 0},
++ {0x4d03, 0x93, 0, 0},
++ {0x4d04, 0xf5, 0, 0},
++ {0x4d05, 0xc1, 0, 0},
++ {0x5000, 0xf3, 0, 0},
++ {0x5001, 0x11, 0, 0},
++ {0x5004, 0x00, 0, 0},
++ {0x500a, 0x00, 0, 0},
++ {0x500b, 0x00, 0, 0},
++ {0x5032, 0x00, 0, 0},
++ {0x5040, 0x00, 0, 0},
++ {0x5050, 0x3c, 0, 0},
++ {0x5500, 0x00, 0, 0},
++ {0x5501, 0x10, 0, 0},
++ {0x5502, 0x01, 0, 0},
++ {0x5503, 0x0f, 0, 0},
++ {0x8000, 0x00, 0, 0},
++ {0x8001, 0x00, 0, 0},
++ {0x8002, 0x00, 0, 0},
++ {0x8003, 0x00, 0, 0},
++ {0x8004, 0x00, 0, 0},
++ {0x8005, 0x00, 0, 0},
++ {0x8006, 0x00, 0, 0},
++ {0x8007, 0x00, 0, 0},
++ {0x8008, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_720P_1280_720[] = {
++ //@@ RES_1280x720_2x_Bin_150fps_816Mbps
++ //OV4689_AM01B_1280x720_24M_2lane_816Mbps_150fps_20140210.txt
++ {0x0103, 0x01, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x0300, 0x00, 0, 0}, // 00
++ {0x0302, 0x22, 0, 0}, // 816Mbps 5a ; 64 ; 5a ; 78 ; 78 ; 2a
++ {0x0303, 0x00, 0, 0}, // 03 ; 01 ; 02 ;
++ {0x0304, 0x03, 0, 0},
++ {0x030b, 0x00, 0, 0},
++ {0x030d, 0x1e, 0, 0},
++ {0x030e, 0x04, 0, 0},
++ {0x030f, 0x01, 0, 0},
++ {0x0312, 0x01, 0, 0},
++ {0x031e, 0x00, 0, 0},
++ {0x3000, 0x20, 0, 0},
++ {0x3002, 0x00, 0, 0},
++ {0x3020, 0x93, 0, 0},
++ {0x3021, 0x03, 0, 0},
++ {0x3022, 0x01, 0, 0},
++ {0x3031, 0x0a, 0, 0},
++ {0x303f, 0x0c, 0, 0},
++ {0x3305, 0xf1, 0, 0},
++ {0x3307, 0x04, 0, 0},
++ {0x3309, 0x29, 0, 0},
++ {0x3500, 0x00, 0, 0},
++ {0x3501, 0x30, 0, 0},
++ {0x3502, 0x00, 0, 0},
++ {0x3503, 0x04, 0, 0},
++ {0x3504, 0x00, 0, 0},
++ {0x3505, 0x00, 0, 0},
++ {0x3506, 0x00, 0, 0},
++ {0x3507, 0x00, 0, 0},
++ {0x3508, 0x07, 0, 0},
++ {0x3509, 0x78, 0, 0}, // 8X
++ {0x350a, 0x00, 0, 0},
++ {0x350b, 0x00, 0, 0},
++ {0x350c, 0x00, 0, 0},
++ {0x350d, 0x00, 0, 0},
++ {0x350e, 0x00, 0, 0},
++ {0x350f, 0x80, 0, 0},
++ {0x3510, 0x00, 0, 0},
++ {0x3511, 0x00, 0, 0},
++ {0x3512, 0x00, 0, 0},
++ {0x3513, 0x00, 0, 0},
++ {0x3514, 0x00, 0, 0},
++ {0x3515, 0x80, 0, 0},
++ {0x3516, 0x00, 0, 0},
++ {0x3517, 0x00, 0, 0},
++ {0x3518, 0x00, 0, 0},
++ {0x3519, 0x00, 0, 0},
++ {0x351a, 0x00, 0, 0},
++ {0x351b, 0x80, 0, 0},
++ {0x351c, 0x00, 0, 0},
++ {0x351d, 0x00, 0, 0},
++ {0x351e, 0x00, 0, 0},
++ {0x351f, 0x00, 0, 0},
++ {0x3520, 0x00, 0, 0},
++ {0x3521, 0x80, 0, 0},
++ {0x3522, 0x08, 0, 0},
++ {0x3524, 0x08, 0, 0},
++ {0x3526, 0x08, 0, 0},
++ {0x3528, 0x08, 0, 0},
++ {0x352a, 0x08, 0, 0},
++ {0x3602, 0x00, 0, 0},
++ {0x3603, 0x40, 0, 0},
++ {0x3604, 0x02, 0, 0},
++ {0x3605, 0x00, 0, 0},
++ {0x3606, 0x00, 0, 0},
++ {0x3607, 0x00, 0, 0},
++ {0x3609, 0x12, 0, 0},
++ {0x360a, 0x40, 0, 0},
++ {0x360c, 0x08, 0, 0},
++ {0x360f, 0xe5, 0, 0},
++ {0x3608, 0x8f, 0, 0},
++ {0x3611, 0x00, 0, 0},
++ {0x3613, 0xf7, 0, 0},
++ {0x3616, 0x58, 0, 0},
++ {0x3619, 0x99, 0, 0},
++ {0x361b, 0x60, 0, 0},
++ {0x361c, 0x7a, 0, 0},
++ {0x361e, 0x79, 0, 0},
++ {0x361f, 0x02, 0, 0},
++ {0x3632, 0x05, 0, 0},
++ {0x3633, 0x10, 0, 0},
++ {0x3634, 0x10, 0, 0},
++ {0x3635, 0x10, 0, 0},
++ {0x3636, 0x15, 0, 0},
++ {0x3646, 0x86, 0, 0},
++ {0x364a, 0x0b, 0, 0},
++ {0x3700, 0x17, 0, 0},
++ {0x3701, 0x22, 0, 0},
++ {0x3703, 0x10, 0, 0},
++ {0x370a, 0x37, 0, 0},
++ {0x3705, 0x00, 0, 0},
++ {0x3706, 0x63, 0, 0},
++ {0x3709, 0x3c, 0, 0},
++ {0x370b, 0x01, 0, 0},
++ {0x370c, 0x30, 0, 0},
++ {0x3710, 0x24, 0, 0},
++ {0x3711, 0x0c, 0, 0},
++ {0x3716, 0x00, 0, 0},
++ {0x3720, 0x28, 0, 0},
++ {0x3729, 0x7b, 0, 0},
++ {0x372a, 0x84, 0, 0},
++ {0x372b, 0xbd, 0, 0},
++ {0x372c, 0xbc, 0, 0},
++ {0x372e, 0x52, 0, 0},
++ {0x373c, 0x0e, 0, 0},
++ {0x373e, 0x33, 0, 0},
++ {0x3743, 0x10, 0, 0},
++ {0x3744, 0x88, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x374a, 0x43, 0, 0},
++ {0x374c, 0x00, 0, 0},
++ {0x374e, 0x23, 0, 0},
++ {0x3751, 0x7b, 0, 0},
++ {0x3752, 0x84, 0, 0},
++ {0x3753, 0xbd, 0, 0},
++ {0x3754, 0xbc, 0, 0},
++ {0x3756, 0x52, 0, 0},
++ {0x375c, 0x00, 0, 0},
++ {0x3760, 0x00, 0, 0},
++ {0x3761, 0x00, 0, 0},
++ {0x3762, 0x00, 0, 0},
++ {0x3763, 0x00, 0, 0},
++ {0x3764, 0x00, 0, 0},
++ {0x3767, 0x04, 0, 0},
++ {0x3768, 0x04, 0, 0},
++ {0x3769, 0x08, 0, 0},
++ {0x376a, 0x08, 0, 0},
++ {0x376b, 0x40, 0, 0},
++ {0x376c, 0x00, 0, 0},
++ {0x376d, 0x00, 0, 0},
++ {0x376e, 0x00, 0, 0},
++ {0x3773, 0x00, 0, 0},
++ {0x3774, 0x51, 0, 0},
++ {0x3776, 0xbd, 0, 0},
++ {0x3777, 0xbd, 0, 0},
++ {0x3781, 0x18, 0, 0},
++ {0x3783, 0x25, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++ {0x3800, 0x00, 0, 0},
++ {0x3801, 0x48, 0, 0},
++ {0x3802, 0x00, 0, 0},
++ {0x3803, 0x2C, 0, 0},
++ {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x57, 0, 0},
++ {0x3806, 0x05, 0, 0},
++ {0x3807, 0xD3, 0, 0},
++ {0x3808, 0x05, 0, 0},
++ {0x3809, 0x00, 0, 0},
++ {0x380a, 0x02, 0, 0},
++ {0x380b, 0xD0, 0, 0},
++#ifndef UNUSED_CODE
++ {0x380c, 0x04, 0, 0}, // 0a ; 03
++ {0x380d, 0x08, 0, 0}, // 1c ; 5C
++#else
++ {0x380c, 0x05, 0, 0}, // 120fps
++ {0x380d, 0x0A, 0, 0},
++#endif
++ {0x380e, 0x03, 0, 0},
++ {0x380f, 0x05, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x04, 0, 0},
++ {0x3812, 0x00, 0, 0},
++ {0x3813, 0x02, 0, 0},
++ {0x3814, 0x03, 0, 0},
++ {0x3815, 0x01, 0, 0},
++ {0x3819, 0x01, 0, 0},
++ {0x3820, 0x06, 0, 0},
++ {0x3821, 0x00, 0, 0},
++ {0x3829, 0x00, 0, 0},
++ {0x382a, 0x03, 0, 0},
++ {0x382b, 0x01, 0, 0},
++ {0x382d, 0x7f, 0, 0},
++ {0x3830, 0x08, 0, 0},
++ {0x3836, 0x02, 0, 0},
++ {0x3837, 0x00, 0, 0},
++ {0x3841, 0x02, 0, 0},
++ {0x3846, 0x08, 0, 0},
++ {0x3847, 0x07, 0, 0},
++ {0x3d85, 0x36, 0, 0},
++ {0x3d8c, 0x71, 0, 0},
++ {0x3d8d, 0xcb, 0, 0},
++ {0x3f0a, 0x00, 0, 0},
++ {0x4000, 0x71, 0, 0},
++ {0x4001, 0x50, 0, 0},
++ {0x4002, 0x04, 0, 0},
++ {0x4003, 0x14, 0, 0},
++ {0x400e, 0x00, 0, 0},
++ {0x4011, 0x00, 0, 0},
++ {0x401a, 0x00, 0, 0},
++ {0x401b, 0x00, 0, 0},
++ {0x401c, 0x00, 0, 0},
++ {0x401d, 0x00, 0, 0},
++ {0x401f, 0x00, 0, 0},
++ {0x4020, 0x00, 0, 0},
++ {0x4021, 0x10, 0, 0},
++ {0x4022, 0x03, 0, 0},
++ {0x4023, 0x93, 0, 0},
++ {0x4024, 0x04, 0, 0},
++ {0x4025, 0xC0, 0, 0},
++ {0x4026, 0x04, 0, 0},
++ {0x4027, 0xD0, 0, 0},
++ {0x4028, 0x00, 0, 0},
++ {0x4029, 0x02, 0, 0},
++ {0x402a, 0x06, 0, 0},
++ {0x402b, 0x04, 0, 0},
++ {0x402c, 0x02, 0, 0},
++ {0x402d, 0x02, 0, 0},
++ {0x402e, 0x0e, 0, 0},
++ {0x402f, 0x04, 0, 0},
++ {0x4302, 0xff, 0, 0},
++ {0x4303, 0xff, 0, 0},
++ {0x4304, 0x00, 0, 0},
++ {0x4305, 0x00, 0, 0},
++ {0x4306, 0x00, 0, 0},
++ {0x4308, 0x02, 0, 0},
++ {0x4500, 0x6c, 0, 0},
++ {0x4501, 0xc4, 0, 0},
++ {0x4502, 0x44, 0, 0},
++ {0x4503, 0x01, 0, 0},
++ {0x4600, 0x00, 0, 0},
++ {0x4601, 0x4F, 0, 0},
++ {0x4800, 0x04, 0, 0},
++ {0x4813, 0x08, 0, 0},
++ {0x481f, 0x40, 0, 0},
++ {0x4829, 0x78, 0, 0},
++ {0x4837, 0x10, 0, 0}, // 20 ; 10
++ {0x4b00, 0x2a, 0, 0},
++ {0x4b0d, 0x00, 0, 0},
++ {0x4d00, 0x04, 0, 0},
++ {0x4d01, 0x42, 0, 0},
++ {0x4d02, 0xd1, 0, 0},
++ {0x4d03, 0x93, 0, 0},
++ {0x4d04, 0xf5, 0, 0},
++ {0x4d05, 0xc1, 0, 0},
++ {0x5000, 0xf3, 0, 0},
++ {0x5001, 0x11, 0, 0},
++ {0x5004, 0x00, 0, 0},
++ {0x500a, 0x00, 0, 0},
++ {0x500b, 0x00, 0, 0},
++ {0x5032, 0x00, 0, 0},
++ {0x5040, 0x00, 0, 0},
++ {0x5050, 0x3c, 0, 0},
++ {0x5500, 0x00, 0, 0},
++ {0x5501, 0x10, 0, 0},
++ {0x5502, 0x01, 0, 0},
++ {0x5503, 0x0f, 0, 0},
++ {0x8000, 0x00, 0, 0},
++ {0x8001, 0x00, 0, 0},
++ {0x8002, 0x00, 0, 0},
++ {0x8003, 0x00, 0, 0},
++ {0x8004, 0x00, 0, 0},
++ {0x8005, 0x00, 0, 0},
++ {0x8006, 0x00, 0, 0},
++ {0x8007, 0x00, 0, 0},
++ {0x8008, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_1080P_1920_1080[] = {
++ //@@ RES_1920x1080_60fps_816Mbps 2lanes
++ {0x0103, 0x01, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x0300, 0x00, 0, 0}, // clk
++ {0x0302, 0x22, 0, 0},
++ {0x0303, 0x00, 0, 0},
++ {0x0304, 0x03, 0, 0},
++ {0x030b, 0x00, 0, 0},
++ {0x030d, 0x1e, 0, 0},
++ {0x030e, 0x04, 0, 0},
++ {0x030f, 0x01, 0, 0},
++ {0x0312, 0x01, 0, 0},
++ {0x031e, 0x00, 0, 0},
++ {0x3000, 0x20, 0, 0},
++ {0x3002, 0x00, 0, 0},
++ {0x3020, 0x93, 0, 0},
++ {0x3021, 0x03, 0, 0},
++ {0x3022, 0x01, 0, 0},
++ {0x3031, 0x0a, 0, 0},
++ {0x303f, 0x0c, 0, 0},
++ {0x3305, 0xf1, 0, 0},
++ {0x3307, 0x04, 0, 0},
++ {0x3309, 0x29, 0, 0},
++ {0x3500, 0x00, 0, 0}, // AEC
++ {0x3501, 0x4c, 0, 0},
++ {0x3502, 0x00, 0, 0},
++ {0x3503, 0x04, 0, 0},
++ {0x3504, 0x00, 0, 0},
++ {0x3505, 0x00, 0, 0},
++ {0x3506, 0x00, 0, 0},
++ {0x3507, 0x00, 0, 0},
++ {0x3508, 0x00, 0, 0},
++ {0x3509, 0x80, 0, 0},
++ {0x350a, 0x00, 0, 0},
++ {0x350b, 0x00, 0, 0},
++ {0x350c, 0x00, 0, 0},
++ {0x350d, 0x00, 0, 0},
++ {0x350e, 0x00, 0, 0},
++ {0x350f, 0x80, 0, 0},
++ {0x3510, 0x00, 0, 0},
++ {0x3511, 0x00, 0, 0},
++ {0x3512, 0x00, 0, 0},
++ {0x3513, 0x00, 0, 0},
++ {0x3514, 0x00, 0, 0},
++ {0x3515, 0x80, 0, 0},
++ {0x3516, 0x00, 0, 0},
++ {0x3517, 0x00, 0, 0},
++ {0x3518, 0x00, 0, 0},
++ {0x3519, 0x00, 0, 0},
++ {0x351a, 0x00, 0, 0},
++ {0x351b, 0x80, 0, 0},
++ {0x351c, 0x00, 0, 0},
++ {0x351d, 0x00, 0, 0},
++ {0x351e, 0x00, 0, 0},
++ {0x351f, 0x00, 0, 0},
++ {0x3520, 0x00, 0, 0},
++ {0x3521, 0x80, 0, 0},
++ {0x3522, 0x08, 0, 0},
++ {0x3524, 0x08, 0, 0},
++ {0x3526, 0x08, 0, 0},
++ {0x3528, 0x08, 0, 0},
++ {0x352a, 0x08, 0, 0},
++ {0x3602, 0x00, 0, 0},
++ {0x3603, 0x40, 0, 0},
++ {0x3604, 0x02, 0, 0},
++ {0x3605, 0x00, 0, 0},
++ {0x3606, 0x00, 0, 0},
++ {0x3607, 0x00, 0, 0},
++ {0x3609, 0x12, 0, 0},
++ {0x360a, 0x40, 0, 0},
++ {0x360c, 0x08, 0, 0},
++ {0x360f, 0xe5, 0, 0},
++ {0x3608, 0x8f, 0, 0},
++ {0x3611, 0x00, 0, 0},
++ {0x3613, 0xf7, 0, 0},
++ {0x3616, 0x58, 0, 0},
++ {0x3619, 0x99, 0, 0},
++ {0x361b, 0x60, 0, 0},
++ {0x361c, 0x7a, 0, 0},
++ {0x361e, 0x79, 0, 0},
++ {0x361f, 0x02, 0, 0},
++ {0x3632, 0x00, 0, 0},
++ {0x3633, 0x10, 0, 0},
++ {0x3634, 0x10, 0, 0},
++ {0x3635, 0x10, 0, 0},
++ {0x3636, 0x15, 0, 0},
++ {0x3646, 0x86, 0, 0},
++ {0x364a, 0x0b, 0, 0},
++ {0x3700, 0x17, 0, 0},
++ {0x3701, 0x22, 0, 0},
++ {0x3703, 0x10, 0, 0},
++ {0x370a, 0x37, 0, 0},
++ {0x3705, 0x00, 0, 0},
++ {0x3706, 0x63, 0, 0},
++ {0x3709, 0x3c, 0, 0},
++ {0x370b, 0x01, 0, 0},
++ {0x370c, 0x30, 0, 0},
++ {0x3710, 0x24, 0, 0},
++ {0x3711, 0x0c, 0, 0},
++ {0x3716, 0x00, 0, 0},
++ {0x3720, 0x28, 0, 0},
++ {0x3729, 0x7b, 0, 0},
++ {0x372a, 0x84, 0, 0},
++ {0x372b, 0xbd, 0, 0},
++ {0x372c, 0xbc, 0, 0},
++ {0x372e, 0x52, 0, 0},
++ {0x373c, 0x0e, 0, 0},
++ {0x373e, 0x33, 0, 0},
++ {0x3743, 0x10, 0, 0},
++ {0x3744, 0x88, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x374a, 0x43, 0, 0},
++ {0x374c, 0x00, 0, 0},
++ {0x374e, 0x23, 0, 0},
++ {0x3751, 0x7b, 0, 0},
++ {0x3752, 0x84, 0, 0},
++ {0x3753, 0xbd, 0, 0},
++ {0x3754, 0xbc, 0, 0},
++ {0x3756, 0x52, 0, 0},
++ {0x375c, 0x00, 0, 0},
++ {0x3760, 0x00, 0, 0},
++ {0x3761, 0x00, 0, 0},
++ {0x3762, 0x00, 0, 0},
++ {0x3763, 0x00, 0, 0},
++ {0x3764, 0x00, 0, 0},
++ {0x3767, 0x04, 0, 0},
++ {0x3768, 0x04, 0, 0},
++ {0x3769, 0x08, 0, 0},
++ {0x376a, 0x08, 0, 0},
++ {0x376b, 0x20, 0, 0},
++ {0x376c, 0x00, 0, 0},
++ {0x376d, 0x00, 0, 0},
++ {0x376e, 0x00, 0, 0},
++ {0x3773, 0x00, 0, 0},
++ {0x3774, 0x51, 0, 0},
++ {0x3776, 0xbd, 0, 0},
++ {0x3777, 0xbd, 0, 0},
++ {0x3781, 0x18, 0, 0},
++ {0x3783, 0x25, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++ {0x3800, 0x01, 0, 0}, // timings
++ {0x3801, 0x88, 0, 0},
++ {0x3802, 0x00, 0, 0},
++ {0x3803, 0xe0, 0, 0},
++ {0x3804, 0x09, 0, 0},
++ {0x3805, 0x17, 0, 0},
++ {0x3806, 0x05, 0, 0},
++ {0x3807, 0x1f, 0, 0},
++ {0x3808, 0x07, 0, 0},
++ {0x3809, 0x80, 0, 0},
++ {0x380a, 0x04, 0, 0},
++ {0x380b, 0x38, 0, 0},
++ {0x380c, 0x06, 0, 0},
++ {0x380d, 0xe0, 0, 0},
++ {0x380e, 0x04, 0, 0},
++ {0x380f, 0x70, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x08, 0, 0},
++ {0x3812, 0x00, 0, 0},
++ {0x3813, 0x04, 0, 0},
++ {0x3814, 0x01, 0, 0},
++ {0x3815, 0x01, 0, 0},
++ {0x3819, 0x01, 0, 0},
++ {0x3820, 0x06, 0, 0},
++ {0x3821, 0x00, 0, 0},
++ {0x3829, 0x00, 0, 0},
++ {0x382a, 0x01, 0, 0},
++ {0x382b, 0x01, 0, 0},
++ {0x382d, 0x7f, 0, 0},
++ {0x3830, 0x04, 0, 0},
++ {0x3836, 0x01, 0, 0},
++ {0x3837, 0x00, 0, 0},
++ {0x3841, 0x02, 0, 0},
++ {0x3846, 0x08, 0, 0},
++ {0x3847, 0x07, 0, 0},
++ {0x3d85, 0x36, 0, 0},
++ {0x3d8c, 0x71, 0, 0},
++ {0x3d8d, 0xcb, 0, 0},
++ {0x3f0a, 0x00, 0, 0},
++ {0x4000, 0xf1, 0, 0},
++ {0x4001, 0x40, 0, 0},
++ {0x4002, 0x04, 0, 0},
++ {0x4003, 0x14, 0, 0},
++ {0x400e, 0x00, 0, 0},
++ {0x4011, 0x00, 0, 0},
++ {0x401a, 0x00, 0, 0},
++ {0x401b, 0x00, 0, 0},
++ {0x401c, 0x00, 0, 0},
++ {0x401d, 0x00, 0, 0},
++ {0x401f, 0x00, 0, 0},
++ {0x4020, 0x00, 0, 0},
++ {0x4021, 0x10, 0, 0},
++ {0x4022, 0x06, 0, 0},
++ {0x4023, 0x13, 0, 0},
++ {0x4024, 0x07, 0, 0},
++ {0x4025, 0x40, 0, 0},
++ {0x4026, 0x07, 0, 0},
++ {0x4027, 0x50, 0, 0},
++ {0x4028, 0x00, 0, 0},
++ {0x4029, 0x02, 0, 0},
++ {0x402a, 0x06, 0, 0},
++ {0x402b, 0x04, 0, 0},
++ {0x402c, 0x02, 0, 0},
++ {0x402d, 0x02, 0, 0},
++ {0x402e, 0x0e, 0, 0},
++ {0x402f, 0x04, 0, 0},
++ {0x4302, 0xff, 0, 0},
++ {0x4303, 0xff, 0, 0},
++ {0x4304, 0x00, 0, 0},
++ {0x4305, 0x00, 0, 0},
++ {0x4306, 0x00, 0, 0},
++ {0x4308, 0x02, 0, 0},
++ {0x4500, 0x6c, 0, 0},
++ {0x4501, 0xc4, 0, 0},
++ {0x4502, 0x40, 0, 0},
++ {0x4503, 0x01, 0, 0},
++ {0x4601, 0x77, 0, 0},
++ {0x4800, 0x04, 0, 0},
++ {0x4813, 0x08, 0, 0},
++ {0x481f, 0x40, 0, 0},
++ {0x4829, 0x78, 0, 0},
++ {0x4837, 0x10, 0, 0},
++ {0x4b00, 0x2a, 0, 0},
++ {0x4b0d, 0x00, 0, 0},
++ {0x4d00, 0x04, 0, 0},
++ {0x4d01, 0x42, 0, 0},
++ {0x4d02, 0xd1, 0, 0},
++ {0x4d03, 0x93, 0, 0},
++ {0x4d04, 0xf5, 0, 0},
++ {0x4d05, 0xc1, 0, 0},
++ {0x5000, 0xf3, 0, 0},
++ {0x5001, 0x11, 0, 0},
++ {0x5004, 0x00, 0, 0},
++ {0x500a, 0x00, 0, 0},
++ {0x500b, 0x00, 0, 0},
++ {0x5032, 0x00, 0, 0},
++ {0x5040, 0x00, 0, 0},
++ {0x5050, 0x0c, 0, 0},
++ {0x5500, 0x00, 0, 0},
++ {0x5501, 0x10, 0, 0},
++ {0x5502, 0x01, 0, 0},
++ {0x5503, 0x0f, 0, 0},
++ {0x8000, 0x00, 0, 0},
++ {0x8001, 0x00, 0, 0},
++ {0x8002, 0x00, 0, 0},
++ {0x8003, 0x00, 0, 0},
++ {0x8004, 0x00, 0, 0},
++ {0x8005, 0x00, 0, 0},
++ {0x8006, 0x00, 0, 0},
++ {0x8007, 0x00, 0, 0},
++ {0x8008, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_4M_2688_1520[] = {
++ //@@ 0 10 RES_2688x1520_default(60fps)
++ //102 2630 960
++ {0x0103, 0x01, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x0300, 0x00, 0, 0},
++ {0x0302, 0x22, 0, 0}, // 2a ;1008Mbps,23 ;; 840Mbps
++ {0x0304, 0x03, 0, 0},
++ {0x030b, 0x00, 0, 0},
++ {0x030d, 0x1e, 0, 0},
++ {0x030e, 0x04, 0, 0},
++ {0x030f, 0x01, 0, 0},
++ {0x0312, 0x01, 0, 0},
++ {0x031e, 0x00, 0, 0},
++ {0x3000, 0x20, 0, 0},
++ {0x3002, 0x00, 0, 0},
++ {0x3020, 0x93, 0, 0},
++ {0x3021, 0x03, 0, 0},
++ {0x3022, 0x01, 0, 0},
++ {0x3031, 0x0a, 0, 0},
++ {0x303f, 0x0c, 0, 0},
++ {0x3305, 0xf1, 0, 0},
++ {0x3307, 0x04, 0, 0},
++ {0x3309, 0x29, 0, 0},
++ {0x3500, 0x00, 0, 0},
++ {0x3501, 0x60, 0, 0},
++ {0x3502, 0x00, 0, 0},
++ {0x3503, 0x04, 0, 0},
++ {0x3504, 0x00, 0, 0},
++ {0x3505, 0x00, 0, 0},
++ {0x3506, 0x00, 0, 0},
++ {0x3507, 0x00, 0, 0},
++ {0x3508, 0x00, 0, 0},
++ {0x3509, 0x80, 0, 0},
++ {0x350a, 0x00, 0, 0},
++ {0x350b, 0x00, 0, 0},
++ {0x350c, 0x00, 0, 0},
++ {0x350d, 0x00, 0, 0},
++ {0x350e, 0x00, 0, 0},
++ {0x350f, 0x80, 0, 0},
++ {0x3510, 0x00, 0, 0},
++ {0x3511, 0x00, 0, 0},
++ {0x3512, 0x00, 0, 0},
++ {0x3513, 0x00, 0, 0},
++ {0x3514, 0x00, 0, 0},
++ {0x3515, 0x80, 0, 0},
++ {0x3516, 0x00, 0, 0},
++ {0x3517, 0x00, 0, 0},
++ {0x3518, 0x00, 0, 0},
++ {0x3519, 0x00, 0, 0},
++ {0x351a, 0x00, 0, 0},
++ {0x351b, 0x80, 0, 0},
++ {0x351c, 0x00, 0, 0},
++ {0x351d, 0x00, 0, 0},
++ {0x351e, 0x00, 0, 0},
++ {0x351f, 0x00, 0, 0},
++ {0x3520, 0x00, 0, 0},
++ {0x3521, 0x80, 0, 0},
++ {0x3522, 0x08, 0, 0},
++ {0x3524, 0x08, 0, 0},
++ {0x3526, 0x08, 0, 0},
++ {0x3528, 0x08, 0, 0},
++ {0x352a, 0x08, 0, 0},
++ {0x3602, 0x00, 0, 0},
++ {0x3603, 0x40, 0, 0},
++ {0x3604, 0x02, 0, 0},
++ {0x3605, 0x00, 0, 0},
++ {0x3606, 0x00, 0, 0},
++ {0x3607, 0x00, 0, 0},
++ {0x3609, 0x12, 0, 0},
++ {0x360a, 0x40, 0, 0},
++ {0x360c, 0x08, 0, 0},
++ {0x360f, 0xe5, 0, 0},
++ {0x3608, 0x8f, 0, 0},
++ {0x3611, 0x00, 0, 0},
++ {0x3613, 0xf7, 0, 0},
++ {0x3616, 0x58, 0, 0},
++ {0x3619, 0x99, 0, 0},
++ {0x361b, 0x60, 0, 0},
++ {0x361c, 0x7a, 0, 0},
++ {0x361e, 0x79, 0, 0},
++ {0x361f, 0x02, 0, 0},
++ {0x3632, 0x00, 0, 0},
++ {0x3633, 0x10, 0, 0},
++ {0x3634, 0x10, 0, 0},
++ {0x3635, 0x10, 0, 0},
++ {0x3636, 0x15, 0, 0},
++ {0x3646, 0x86, 0, 0},
++ {0x364a, 0x0b, 0, 0},
++ {0x3700, 0x17, 0, 0},
++ {0x3701, 0x22, 0, 0},
++ {0x3703, 0x10, 0, 0},
++ {0x370a, 0x37, 0, 0},
++ {0x3705, 0x00, 0, 0},
++ {0x3706, 0x63, 0, 0},
++ {0x3709, 0x3c, 0, 0},
++ {0x370b, 0x01, 0, 0},
++ {0x370c, 0x30, 0, 0},
++ {0x3710, 0x24, 0, 0},
++ {0x3711, 0x0c, 0, 0},
++ {0x3716, 0x00, 0, 0},
++ {0x3720, 0x28, 0, 0},
++ {0x3729, 0x7b, 0, 0},
++ {0x372a, 0x84, 0, 0},
++ {0x372b, 0xbd, 0, 0},
++ {0x372c, 0xbc, 0, 0},
++ {0x372e, 0x52, 0, 0},
++ {0x373c, 0x0e, 0, 0},
++ {0x373e, 0x33, 0, 0},
++ {0x3743, 0x10, 0, 0},
++ {0x3744, 0x88, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x374a, 0x43, 0, 0},
++ {0x374c, 0x00, 0, 0},
++ {0x374e, 0x23, 0, 0},
++ {0x3751, 0x7b, 0, 0},
++ {0x3752, 0x84, 0, 0},
++ {0x3753, 0xbd, 0, 0},
++ {0x3754, 0xbc, 0, 0},
++ {0x3756, 0x52, 0, 0},
++ {0x375c, 0x00, 0, 0},
++ {0x3760, 0x00, 0, 0},
++ {0x3761, 0x00, 0, 0},
++ {0x3762, 0x00, 0, 0},
++ {0x3763, 0x00, 0, 0},
++ {0x3764, 0x00, 0, 0},
++ {0x3767, 0x04, 0, 0},
++ {0x3768, 0x04, 0, 0},
++ {0x3769, 0x08, 0, 0},
++ {0x376a, 0x08, 0, 0},
++ {0x376b, 0x20, 0, 0},
++ {0x376c, 0x00, 0, 0},
++ {0x376d, 0x00, 0, 0},
++ {0x376e, 0x00, 0, 0},
++ {0x3773, 0x00, 0, 0},
++ {0x3774, 0x51, 0, 0},
++ {0x3776, 0xbd, 0, 0},
++ {0x3777, 0xbd, 0, 0},
++ {0x3781, 0x18, 0, 0},
++ {0x3783, 0x25, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++ {0x3800, 0x00, 0, 0},
++ {0x3801, 0x08, 0, 0},
++ {0x3802, 0x00, 0, 0},
++ {0x3803, 0x04, 0, 0},
++ {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x97, 0, 0},
++ {0x3806, 0x05, 0, 0},
++ {0x3807, 0xfb, 0, 0},
++ {0x3808, 0x0a, 0, 0},
++ {0x3809, 0x80, 0, 0},
++ {0x380a, 0x05, 0, 0},
++ {0x380b, 0xf0, 0, 0},
++ {0x380c, 0x03, 0, 0},
++ {0x380d, 0x5c, 0, 0},
++ {0x380e, 0x06, 0, 0},
++ {0x380f, 0x12, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x08, 0, 0},
++ {0x3812, 0x00, 0, 0},
++ {0x3813, 0x04, 0, 0},
++ {0x3814, 0x01, 0, 0},
++ {0x3815, 0x01, 0, 0},
++ {0x3819, 0x01, 0, 0},
++ {0x3820, 0x00, 0, 0},
++ {0x3821, 0x06, 0, 0},
++ {0x3829, 0x00, 0, 0},
++ {0x382a, 0x01, 0, 0},
++ {0x382b, 0x01, 0, 0},
++ {0x382d, 0x7f, 0, 0},
++ {0x3830, 0x04, 0, 0},
++ {0x3836, 0x01, 0, 0},
++ {0x3837, 0x00, 0, 0},
++ {0x3841, 0x02, 0, 0},
++ {0x3846, 0x08, 0, 0},
++ {0x3847, 0x07, 0, 0},
++ {0x3d85, 0x36, 0, 0},
++ {0x3d8c, 0x71, 0, 0},
++ {0x3d8d, 0xcb, 0, 0},
++ {0x3f0a, 0x00, 0, 0},
++ {0x4000, 0x71, 0, 0},
++ {0x4001, 0x40, 0, 0},
++ {0x4002, 0x04, 0, 0},
++ {0x4003, 0x14, 0, 0},
++ {0x400e, 0x00, 0, 0},
++ {0x4011, 0x00, 0, 0},
++ {0x401a, 0x00, 0, 0},
++ {0x401b, 0x00, 0, 0},
++ {0x401c, 0x00, 0, 0},
++ {0x401d, 0x00, 0, 0},
++ {0x401f, 0x00, 0, 0},
++ {0x4020, 0x00, 0, 0},
++ {0x4021, 0x10, 0, 0},
++ {0x4022, 0x07, 0, 0},
++ {0x4023, 0xcf, 0, 0},
++ {0x4024, 0x09, 0, 0},
++ {0x4025, 0x60, 0, 0},
++ {0x4026, 0x09, 0, 0},
++ {0x4027, 0x6f, 0, 0},
++ {0x4028, 0x00, 0, 0},
++ {0x4029, 0x02, 0, 0},
++ {0x402a, 0x06, 0, 0},
++ {0x402b, 0x04, 0, 0},
++ {0x402c, 0x02, 0, 0},
++ {0x402d, 0x02, 0, 0},
++ {0x402e, 0x0e, 0, 0},
++ {0x402f, 0x04, 0, 0},
++ {0x4302, 0xff, 0, 0},
++ {0x4303, 0xff, 0, 0},
++ {0x4304, 0x00, 0, 0},
++ {0x4305, 0x00, 0, 0},
++ {0x4306, 0x00, 0, 0},
++ {0x4308, 0x02, 0, 0},
++ {0x4500, 0x6c, 0, 0},
++ {0x4501, 0xc4, 0, 0},
++ {0x4502, 0x40, 0, 0},
++ {0x4503, 0x01, 0, 0},
++ {0x4601, 0x04, 0, 0},
++ {0x4800, 0x04, 0, 0},
++ {0x4813, 0x08, 0, 0},
++ {0x481f, 0x40, 0, 0},
++ {0x4829, 0x78, 0, 0},
++ {0x4837, 0x14, 0, 0}, // 10
++ {0x4b00, 0x2a, 0, 0},
++ {0x4b0d, 0x00, 0, 0},
++ {0x4d00, 0x04, 0, 0},
++ {0x4d01, 0x42, 0, 0},
++ {0x4d02, 0xd1, 0, 0},
++ {0x4d03, 0x93, 0, 0},
++ {0x4d04, 0xf5, 0, 0},
++ {0x4d05, 0xc1, 0, 0},
++ {0x5000, 0xf3, 0, 0},
++ {0x5001, 0x11, 0, 0},
++ {0x5004, 0x00, 0, 0},
++ {0x500a, 0x00, 0, 0},
++ {0x500b, 0x00, 0, 0},
++ {0x5032, 0x00, 0, 0},
++ {0x5040, 0x00, 0, 0},
++ {0x5050, 0x0c, 0, 0},
++ {0x5500, 0x00, 0, 0},
++ {0x5501, 0x10, 0, 0},
++ {0x5502, 0x01, 0, 0},
++ {0x5503, 0x0f, 0, 0},
++ {0x8000, 0x00, 0, 0},
++ {0x8001, 0x00, 0, 0},
++ {0x8002, 0x00, 0, 0},
++ {0x8003, 0x00, 0, 0},
++ {0x8004, 0x00, 0, 0},
++ {0x8005, 0x00, 0, 0},
++ {0x8006, 0x00, 0, 0},
++ {0x8007, 0x00, 0, 0},
++ {0x8008, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++// {0x0100, 0x01, 0, 0},
++
++// {0x0100, 0x00, 0, 0},
++ {0x380c, 0x0A, 0, 0}, // 05
++ {0x380d, 0x0A, 0, 0}, // 10
++ {0x380e, 0x06, 0, 0},
++ {0x380f, 0x12, 0, 0},
++// {0x0100, 0x01, 0, 0},
++ {0x3105, 0x31, 0, 0},
++ {0x301a, 0xf9, 0, 0},
++ {0x3508, 0x07, 0, 0},
++ {0x484b, 0x05, 0, 0},
++ {0x4805, 0x03, 0, 0},
++ {0x3601, 0x01, 0, 0},
++ {0x3745, 0xc0, 0, 0},
++ {0x3798, 0x1b, 0, 0},
++// {0x0100, 0x01, 0, 0},
++ {0xffff, 0x0a, 0, 0},
++ {0x3105, 0x11, 0, 0},
++ {0x301a, 0xf1, 0, 0},
++ {0x4805, 0x00, 0, 0},
++ {0x301a, 0xf0, 0, 0},
++ {0x3208, 0x00, 0, 0},
++ {0x302a, 0x00, 0, 0},
++ {0x302a, 0x00, 0, 0},
++ {0x302a, 0x00, 0, 0},
++ {0x302a, 0x00, 0, 0},
++ {0x302a, 0x00, 0, 0},
++ {0x3601, 0x00, 0, 0},
++ {0x3638, 0x00, 0, 0},
++ {0x3208, 0x10, 0, 0},
++ {0x3208, 0xa0, 0, 0},
++};
++
++/* power-on sensor init reg table */
++static const struct ov4689_mode_info ov4689_mode_init_data = {
++
++};
++
++static const struct ov4689_mode_info
++ov4689_mode_data[OV4689_NUM_MODES] = {
++ // {OV4689_MODE_720P_1280_720, SUBSAMPLING,
++ // 1280, 0x408, 720, 0x305,
++ // ov4689_setting_720P_1280_720,
++ // ARRAY_SIZE(ov4689_setting_720P_1280_720),
++ // OV4689_150_FPS},
++ // {OV4689_MODE_1080P_1920_1080, SCALING,
++ // 1920, 0x6e0, 1080, 0x470,
++ // ov4689_setting_1080P_1920_1080,
++ // ARRAY_SIZE(ov4689_setting_1080P_1920_1080),
++ // OV4689_60_FPS},
++ // {OV4689_MODE_4M_2688_1520, SCALING,
++ // 2688, 0xa0a, 1520, 0x612,
++ // ov4689_setting_4M_2688_1520,
++ // ARRAY_SIZE(ov4689_setting_4M_2688_1520),
++ // OV4689_60_FPS},
++
++ {OV4689_MODE_1080P_1920_1080, SCALING,
++ 1920, 0x6e0, 1080, 0x470,
++ ov4689_init_setting_30fps_1080P,
++ ARRAY_SIZE(ov4689_init_setting_30fps_1080P),
++ OV4689_60_FPS},
++};
++
++static int ov4689_write_reg(struct ov4689_dev *sensor, u16 reg, u8 val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg;
++ u8 buf[3];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val;
++
++ msg.addr = client->addr;
++ msg.flags = client->flags;
++ msg.buf = buf;
++ msg.len = sizeof(buf);
++
++ ret = i2c_transfer(client->adapter, &msg, 1);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++ __func__, reg, val);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ov4689_read_reg(struct ov4689_dev *sensor, u16 reg, u8 *val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg[2];
++ u8 buf[2];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++
++ msg[0].addr = client->addr;
++ msg[0].flags = client->flags;
++ msg[0].buf = buf;
++ msg[0].len = sizeof(buf);
++
++ msg[1].addr = client->addr;
++ msg[1].flags = client->flags | I2C_M_RD;
++ msg[1].buf = buf;
++ msg[1].len = 1;
++
++ ret = i2c_transfer(client->adapter, msg, 2);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x\n",
++ __func__, reg);
++ return ret;
++ }
++
++ *val = buf[0];
++ return 0;
++}
++
++static int ov4689_read_reg16(struct ov4689_dev *sensor, u16 reg, u16 *val)
++{
++ u8 hi, lo;
++ int ret;
++
++ ret = ov4689_read_reg(sensor, reg, &hi);
++ if (ret)
++ return ret;
++ ret = ov4689_read_reg(sensor, reg + 1, &lo);
++ if (ret)
++ return ret;
++
++ *val = ((u16)hi << 8) | (u16)lo;
++ return 0;
++}
++
++static int ov4689_write_reg16(struct ov4689_dev *sensor, u16 reg, u16 val)
++{
++ int ret;
++
++ ret = ov4689_write_reg(sensor, reg, val >> 8);
++ if (ret)
++ return ret;
++
++ return ov4689_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov4689_mod_reg(struct ov4689_dev *sensor, u16 reg,
++ u8 mask, u8 val)
++{
++ u8 readval;
++ int ret;
++
++ ret = ov4689_read_reg(sensor, reg, &readval);
++ if (ret)
++ return ret;
++
++ readval &= ~mask;
++ val &= mask;
++ val |= readval;
++
++ return ov4689_write_reg(sensor, reg, val);
++}
++
++static int ov4689_set_timings(struct ov4689_dev *sensor,
++ const struct ov4689_mode_info *mode)
++{
++ return 0;
++}
++
++static int ov4689_load_regs(struct ov4689_dev *sensor,
++ const struct ov4689_mode_info *mode)
++{
++ const struct reg_value *regs = mode->reg_data;
++ unsigned int i;
++ u32 delay_ms;
++ u16 reg_addr;
++ u8 mask, val;
++ int ret = 0;
++
++ st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++ for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++ delay_ms = regs->delay_ms;
++ reg_addr = regs->reg_addr;
++ val = regs->val;
++ mask = regs->mask;
++
++ if (mask)
++ ret = ov4689_mod_reg(sensor, reg_addr, mask, val);
++ else
++ ret = ov4689_write_reg(sensor, reg_addr, val);
++ if (ret)
++ break;
++
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++
++ return ov4689_set_timings(sensor, mode);
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_get_exposure(struct ov4689_dev *sensor)
++{
++ int exp, ret;
++ u8 temp;
++
++ ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_HI, &temp);
++ if (ret)
++ return ret;
++ exp = ((int)temp & 0x0f) << 16;
++ ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_MED, &temp);
++ if (ret)
++ return ret;
++ exp |= ((int)temp << 8);
++ ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_LO, &temp);
++ if (ret)
++ return ret;
++ exp |= (int)temp;
++
++ return exp >> 4;
++}
++#endif
++
++static int ov4689_set_exposure(struct ov4689_dev *sensor, u32 exposure)
++{
++ int ret;
++
++ st_info(ST_SENSOR, "%s, exposure = 0x%x\n", __func__, exposure);
++ exposure <<= 4;
++
++ ret = ov4689_write_reg(sensor,
++ OV4689_REG_EXPOSURE_LO,
++ exposure & 0xff);
++ if (ret)
++ return ret;
++ ret = ov4689_write_reg(sensor,
++ OV4689_REG_EXPOSURE_MED,
++ (exposure >> 8) & 0xff);
++ if (ret)
++ return ret;
++ return ov4689_write_reg(sensor,
++ OV4689_REG_EXPOSURE_HI,
++ (exposure >> 16) & 0x0f);
++}
++
++static int ov4689_get_gain(struct ov4689_dev *sensor)
++{
++ u32 gain = 0;
++ u8 val;
++
++ ov4689_read_reg(sensor, OV4689_REG_GAIN_H, &val);
++ gain = (val & 0x3) << 16;
++ ov4689_read_reg(sensor, OV4689_REG_GAIN_M, &val);
++ gain |= val << 8;
++ ov4689_read_reg(sensor, OV4689_REG_GAIN_L, &val);
++ gain |= val;
++
++ return gain;
++}
++
++static int ov4689_set_gain(struct ov4689_dev *sensor, int gain)
++{
++ ov4689_write_reg(sensor, OV4689_REG_GAIN_H,
++ (gain >> 16) & 0x3);
++ ov4689_write_reg(sensor, OV4689_REG_GAIN_M,
++ (gain >> 8) & 0xff);
++ ov4689_write_reg(sensor, OV4689_REG_GAIN_L,
++ gain & 0xff);
++ return 0;
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_get_sysclk(struct ov4689_dev *sensor)
++{
++ return 0;
++}
++
++static int ov4689_set_night_mode(struct ov4689_dev *sensor)
++{
++ return 0;
++}
++
++static int ov4689_get_hts(struct ov4689_dev *sensor)
++{
++ /* read HTS from register settings */
++ u16 hts;
++ int ret;
++
++ ret = ov4689_read_reg16(sensor, OV4689_REG_TIMING_HTS, &hts);
++ if (ret)
++ return ret;
++ return hts;
++}
++#endif
++
++static int ov4689_get_vts(struct ov4689_dev *sensor)
++{
++ u16 vts;
++ int ret;
++
++ ret = ov4689_read_reg16(sensor, OV4689_REG_TIMING_VTS, &vts);
++ if (ret)
++ return ret;
++ return vts;
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_set_vts(struct ov4689_dev *sensor, int vts)
++{
++ return ov4689_write_reg16(sensor, OV4689_REG_TIMING_VTS, vts);
++}
++
++static int ov4689_get_light_freq(struct ov4689_dev *sensor)
++{
++ return 0;
++}
++
++static int ov4689_set_bandingfilter(struct ov4689_dev *sensor)
++{
++ return 0;
++}
++
++static int ov4689_set_ae_target(struct ov4689_dev *sensor, int target)
++{
++ return 0;
++}
++
++static int ov4689_get_binning(struct ov4689_dev *sensor)
++{
++ return 0;
++}
++
++static int ov4689_set_binning(struct ov4689_dev *sensor, bool enable)
++{
++ return 0;
++}
++#endif
++
++static const struct ov4689_mode_info *
++ov4689_find_mode(struct ov4689_dev *sensor, enum ov4689_frame_rate fr,
++ int width, int height, bool nearest)
++{
++ const struct ov4689_mode_info *mode;
++
++ mode = v4l2_find_nearest_size(ov4689_mode_data,
++ ARRAY_SIZE(ov4689_mode_data),
++ hact, vact,
++ width, height);
++
++ if (!mode ||
++ (!nearest && (mode->hact != width || mode->vact != height)))
++ return NULL;
++
++ /* Check to see if the current mode exceeds the max frame rate */
++ if (ov4689_framerates[fr] > ov4689_framerates[mode->max_fps])
++ return NULL;
++
++ return mode;
++}
++
++static u64 ov4689_calc_pixel_rate(struct ov4689_dev *sensor)
++{
++ u64 rate;
++
++ rate = sensor->current_mode->vact * sensor->current_mode->hact;
++ rate *= ov4689_framerates[sensor->current_fr];
++
++ return rate;
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ * +--------------+
++ * | Ext. Clock |
++ * +-+------------+
++ * | +----------+
++ * +->| PLL1 | - reg 0x030a, bit0 for the pre-dividerp
++ * +-+--------+ - reg 0x0300, bits 0-2 for the pre-divider
++ * +-+--------+ - reg 0x0301~0x0302, for the multiplier
++ * | +--------------+
++ * +->| MIPI Divider | - reg 0x0303, bits 0-3 for the pre-divider
++ * | +---------> MIPI PHY CLK
++ * | +-----+
++ * | +->| PLL1_DIV_MIPI | - reg 0x0304, bits 0-1 for the divider
++ * | +----------------> PCLK
++ * | +-----+
++ *
++ * +--------------+
++ * | Ext. Clock |
++ * +-+------------+
++ * | +----------+
++ * +->| PLL2 | - reg 0x0311, bit0 for the pre-dividerp
++ * +-+--------+ - reg 0x030b, bits 0-2 for the pre-divider
++ * +-+--------+ - reg 0x030c~0x030d, for the multiplier
++ * | +--------------+
++ * +->| SCLK Divider | - reg 0x030F, bits 0-3 for the pre-divider
++ * +-+--------+ - reg 0x030E, bits 0-2 for the divider
++ * | +---------> SCLK
++ *
++ * | +-----+
++ * +->| DAC Divider | - reg 0x0312, bits 0-3 for the divider
++ * | +----------------> DACCLK
++ **
++ */
++
++/*
++ * ov4689_set_mipi_pclk() - Calculate the clock tree configuration values
++ * for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ * 'Bandwidth Per Lane' is calculated as:
++ * bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ *
++ * - mipi_pclk = bpl / 2; ( / 2 is for CSI-2 DDR)
++ * - mipi_phy_clk = mipi_pclk * PLL1_DIV_MIPI;
++ *
++ * with these fixed parameters:
++ * PLL1_PREDIVP = 1;
++ * PLL1_PREDIV = 1; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ * PLL1_DIVM = 1;
++ * PLL1_DIV_MIPI = 4;
++ *
++ * FIXME: this have been tested with 10-bit raw and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++
++#define PLL1_PREDIVP 1 // bypass
++#define PLL1_PREDIV 1 // bypass
++#define PLL1_DIVM 1 // bypass
++#define PLL1_DIV_MIPI 3 // div
++#define PLL1_DIV_MIPI_BASE 1 // div
++
++#define PLL1_DIVSP 1 // no use
++#define PLL1_DIVS 1 // no use
++
++#define PLL2_PREDIVP 0
++#define PLL2_PREDIV 0
++#define PLL2_DIVSP 1
++#define PLL2_DIVS 4
++#define PLL2_DIVDAC 1
++
++#define OV4689_PLL1_PREDIVP 0x030a // bits[0]
++#define OV4689_PLL1_PREDIV 0x0300 // bits[2:0]
++#define OV4689_PLL1_MULTIPLIER 0x0301 // bits[9:8] 0x0302 bits[7:0]
++#define OV4689_PLL1_DIVM 0x0303 // bits[3:0]
++#define OV4689_PLL1_DIV_MIPI 0x0304 // bits[1:0]
++
++#define OV4689_PLL1_DIVSP 0x0305 //bits[1:0]
++#define OV4689_PLL1_DIVS 0x0306 // bits[0]
++
++#define OV4689_PLL2_PREDIVP 0x0311 // bits[0]
++#define OV4689_PLL2_PREDIV 0x030b // bits[2:0]
++#define OV4689_PLL2_MULTIPLIER 0x030c // bits[9:8] 0x030d bits[7:0]
++#define OV4689_PLL2_DIVSP 0x030f // bits[3:0]
++#define OV4689_PLL2_DIVS 0x030e // bits[2:0]
++#define OV4689_PLL2_DIVDAC 0x0312 // bits[3:0]
++
++static int ov4689_set_mipi_pclk(struct ov4689_dev *sensor,
++ unsigned long rate)
++{
++ const struct ov4689_mode_info *mode = sensor->current_mode;
++ //const struct ov4689_mode_info *orig_mode = sensor->last_mode;
++ u8 val;
++ int ret = 0;
++ int fps = ov4689_framerates[sensor->current_fr];
++ u16 htot, val16;
++
++ htot = mode->htot * ov4689_framerates[mode->max_fps] / fps;
++
++ ret = ov4689_write_reg16(sensor, OV4689_REG_TIMING_HTS, htot);
++
++ ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS, &val);
++ val16 = val << 8;
++ ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS + 1, &val);
++ val16 |= val;
++
++ st_info(ST_SENSOR, "fps = %d, max_fps = %d\n", fps, mode->max_fps);
++ st_info(ST_SENSOR, "mode->htot = 0x%x, htot = 0x%x\n", mode->htot,
++ htot);
++ st_info(ST_SENSOR, "reg: 0x%x = 0x%x\n", OV4689_REG_TIMING_HTS, val16);
++
++ return 0;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov4689_set_mode_direct(struct ov4689_dev *sensor,
++ const struct ov4689_mode_info *mode)
++{
++ if (!mode->reg_data)
++ return -EINVAL;
++
++ /* Write capture setting */
++ return ov4689_load_regs(sensor, mode);
++}
++
++static int ov4689_set_mode(struct ov4689_dev *sensor)
++{
++ const struct ov4689_mode_info *mode = sensor->current_mode;
++
++ int ret = 0;
++
++ ret = ov4689_set_mode_direct(sensor, mode);
++ if (ret < 0)
++ return ret;
++
++ ret = ov4689_set_mipi_pclk(sensor, 0);
++ if (ret < 0)
++ return 0;
++
++ sensor->pending_mode_change = false;
++ sensor->last_mode = mode;
++ return 0;
++}
++
++/* restore the last set video mode after chip power-on */
++static int ov4689_restore_mode(struct ov4689_dev *sensor)
++{
++ int ret;
++
++ /* first load the initial register values */
++ ret = ov4689_load_regs(sensor, &ov4689_mode_init_data);
++ if (ret < 0)
++ return ret;
++ sensor->last_mode = &ov4689_mode_init_data;
++
++ /* now restore the last capture mode */
++ ret = ov4689_set_mode(sensor);
++ if (ret < 0)
++ return ret;
++
++ return ret;
++}
++
++static void ov4689_power(struct ov4689_dev *sensor, bool enable)
++{
++ if (!sensor->pwdn_gpio)
++ return;
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void ov4689_reset(struct ov4689_dev *sensor)
++{
++ if (!sensor->reset_gpio)
++ return;
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++ usleep_range(5000, 25000);
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++ usleep_range(1000, 2000);
++}
++
++static int ov4689_set_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ int ret;
++
++ ret = clk_prepare_enable(sensor->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ return ret;
++ }
++
++ ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES,
++ sensor->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ goto xclk_off;
++ }
++
++ ov4689_reset(sensor);
++ ov4689_power(sensor, true);
++
++ return 0;
++
++xclk_off:
++ clk_disable_unprepare(sensor->xclk);
++ return ret;
++}
++
++static int ov4689_set_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++ ov4689_power(sensor, false);
++ regulator_bulk_disable(OV4689_NUM_SUPPLIES, sensor->supplies);
++ clk_disable_unprepare(sensor->xclk);
++
++ return 0;
++}
++
++static int ov4689_try_frame_interval(struct ov4689_dev *sensor,
++ struct v4l2_fract *fi,
++ u32 width, u32 height)
++{
++ const struct ov4689_mode_info *mode;
++ enum ov4689_frame_rate rate = OV4689_15_FPS;
++ int minfps, maxfps, best_fps, fps;
++ int i;
++
++ minfps = ov4689_framerates[OV4689_15_FPS];
++ maxfps = ov4689_framerates[OV4689_NUM_FRAMERATES - 1];
++
++ if (fi->numerator == 0) {
++ fi->denominator = maxfps;
++ fi->numerator = 1;
++ rate = OV4689_60_FPS;
++ goto find_mode;
++ }
++
++ fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++ minfps, maxfps);
++
++ best_fps = minfps;
++ for (i = 0; i < ARRAY_SIZE(ov4689_framerates); i++) {
++ int curr_fps = ov4689_framerates[i];
++
++ if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++ best_fps = curr_fps;
++ rate = i;
++ }
++ }
++ st_info(ST_SENSOR, "best_fps = %d, fps = %d\n", best_fps, fps);
++
++ fi->numerator = 1;
++ fi->denominator = best_fps;
++
++find_mode:
++ mode = ov4689_find_mode(sensor, rate, width, height, false);
++ return mode ? rate : -EINVAL;
++}
++
++static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ if (code->pad != 0)
++ return -EINVAL;
++
++ if (code->index)
++ return -EINVAL;
++
++ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ return 0;
++}
++
++static int ov4689_get_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ struct v4l2_mbus_framefmt *fmt;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++ format->pad);
++ else
++ fmt = &sensor->fmt;
++
++ format->format = *fmt;
++
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov4689_try_fmt_internal(struct v4l2_subdev *sd,
++ struct v4l2_mbus_framefmt *fmt,
++ enum ov4689_frame_rate fr,
++ const struct ov4689_mode_info **new_mode)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ const struct ov4689_mode_info *mode;
++
++ mode = ov4689_find_mode(sensor, fr, fmt->width, fmt->height, true);
++ if (!mode)
++ return -EINVAL;
++ fmt->width = mode->hact;
++ fmt->height = mode->vact;
++
++ if (new_mode)
++ *new_mode = mode;
++
++ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++
++ return 0;
++}
++
++static int ov4689_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ const struct ov4689_mode_info *new_mode;
++ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++ struct v4l2_mbus_framefmt *fmt;
++ int ret;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ ret = ov4689_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++ if (ret)
++ goto out;
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(sd, state, 0);
++ else
++ fmt = &sensor->fmt;
++
++ *fmt = *mbus_fmt;
++
++ if (new_mode != sensor->current_mode) {
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++ if (new_mode->max_fps < sensor->current_fr) {
++ sensor->current_fr = new_mode->max_fps;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator =
++ ov4689_framerates[sensor->current_fr];
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov4689_calc_pixel_rate(sensor));
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov4689_set_ctrl_hue(struct ov4689_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov4689_set_ctrl_contrast(struct ov4689_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov4689_set_ctrl_saturation(struct ov4689_dev *sensor, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int ov4689_set_ctrl_white_balance(struct ov4689_dev *sensor, int awb)
++{
++ struct ov4689_ctrls *ctrls = &sensor->ctrls;
++ int ret = 0;
++
++ if (!awb && (ctrls->red_balance->is_new
++ || ctrls->blue_balance->is_new)) {
++ u16 red = (u16)ctrls->red_balance->val;
++ u16 blue = (u16)ctrls->blue_balance->val;
++
++ st_info(ST_SENSOR, "red = 0x%x, blue = 0x%x\n", red, blue);
++ ret = ov4689_write_reg16(sensor, OV4689_REG_AWB_R_GAIN, red);
++ if (ret)
++ return ret;
++ ret = ov4689_write_reg16(sensor, OV4689_REG_AWB_B_GAIN, blue);
++ }
++ return ret;
++}
++
++static int ov4689_set_ctrl_exposure(struct ov4689_dev *sensor,
++ enum v4l2_exposure_auto_type auto_exposure)
++{
++ struct ov4689_ctrls *ctrls = &sensor->ctrls;
++ bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++ int ret = 0;
++
++ if (!auto_exp && ctrls->exposure->is_new) {
++ u16 max_exp = 0;
++
++ ret = ov4689_read_reg16(sensor, OV4689_REG_V_OUTPUT_SIZE,
++ &max_exp);
++
++ ret = ov4689_get_vts(sensor);
++ if (ret < 0)
++ return ret;
++ max_exp += ret;
++ ret = 0;
++
++ st_info(ST_SENSOR, "%s, max_exp = 0x%x\n", __func__, max_exp);
++ if (ctrls->exposure->val < max_exp)
++ ret = ov4689_set_exposure(sensor, ctrls->exposure->val);
++ }
++
++ return ret;
++}
++
++static const s64 link_freq_menu_items[] = {
++ OV4689_LINK_FREQ_500MHZ
++};
++
++static const char * const test_pattern_menu[] = {
++ "Disabled",
++ "Color bars",
++ "Color bars w/ rolling bar",
++ "Color squares",
++ "Color squares w/ rolling bar",
++};
++
++#define OV4689_TEST_ENABLE BIT(7)
++#define OV4689_TEST_ROLLING BIT(6) /* rolling horizontal bar */
++#define OV4689_TEST_TRANSPARENT BIT(5)
++#define OV4689_TEST_SQUARE_BW BIT(4) /* black & white squares */
++#define OV4689_TEST_BAR_STANDARD (0 << 2)
++#define OV4689_TEST_BAR_DARKER_1 (1 << 2)
++#define OV4689_TEST_BAR_DARKER_2 (2 << 2)
++#define OV4689_TEST_BAR_DARKER_3 (3 << 2)
++#define OV4689_TEST_BAR (0 << 0)
++#define OV4689_TEST_RANDOM (1 << 0)
++#define OV4689_TEST_SQUARE (2 << 0)
++#define OV4689_TEST_BLACK (3 << 0)
++
++static const u8 test_pattern_val[] = {
++ 0,
++ OV4689_TEST_ENABLE | OV4689_TEST_BAR_STANDARD |
++ OV4689_TEST_BAR,
++ OV4689_TEST_ENABLE | OV4689_TEST_ROLLING |
++ OV4689_TEST_BAR_DARKER_1 | OV4689_TEST_BAR,
++ OV4689_TEST_ENABLE | OV4689_TEST_SQUARE,
++ OV4689_TEST_ENABLE | OV4689_TEST_ROLLING | OV4689_TEST_SQUARE,
++};
++
++static int ov4689_set_ctrl_test_pattern(struct ov4689_dev *sensor, int value)
++{
++ return ov4689_write_reg(sensor, OV4689_REG_TEST_PATTERN,
++ test_pattern_val[value]);
++}
++
++static int ov4689_set_ctrl_light_freq(struct ov4689_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int ov4689_set_ctrl_hflip(struct ov4689_dev *sensor, int value)
++{
++ /*
++ * TIMING TC REG21:
++ * - [2]: Digital mirror
++ * - [1]: Array mirror
++ */
++ return ov4689_mod_reg(sensor, OV4689_REG_TIMING_TC_REG21,
++ BIT(2) | BIT(1),
++ (!(value ^ sensor->upside_down)) ?
++ (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov4689_set_ctrl_vflip(struct ov4689_dev *sensor, int value)
++{
++ /*
++ * TIMING TC REG20:
++ * - [2]: Digital vflip
++ * - [1]: Array vflip
++ */
++ return ov4689_mod_reg(sensor, OV4689_REG_TIMING_TC_REG20,
++ BIT(2) | BIT(1),
++ (value ^ sensor->upside_down) ?
++ (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov4689_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ int val;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ val = ov4689_get_gain(sensor);
++ break;
++ }
++
++ pm_runtime_put(&sensor->i2c_client->dev);
++
++ return 0;
++}
++
++static int ov4689_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ int ret;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored at start streaming time.
++ */
++ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = ov4689_set_gain(sensor, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = ov4689_set_ctrl_exposure(sensor, V4L2_EXPOSURE_MANUAL);
++ break;
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = ov4689_set_ctrl_white_balance(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HUE:
++ ret = ov4689_set_ctrl_hue(sensor, ctrl->val);
++ break;
++ case V4L2_CID_CONTRAST:
++ ret = ov4689_set_ctrl_contrast(sensor, ctrl->val);
++ break;
++ case V4L2_CID_SATURATION:
++ ret = ov4689_set_ctrl_saturation(sensor, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = ov4689_set_ctrl_test_pattern(sensor, ctrl->val);
++ break;
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ ret = ov4689_set_ctrl_light_freq(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = ov4689_set_ctrl_hflip(sensor, ctrl->val);
++ break;
++ case V4L2_CID_VFLIP:
++ ret = ov4689_set_ctrl_vflip(sensor, ctrl->val);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_put(&sensor->i2c_client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
++ .g_volatile_ctrl = ov4689_g_volatile_ctrl,
++ .s_ctrl = ov4689_s_ctrl,
++};
++
++static int ov4689_init_controls(struct ov4689_dev *sensor)
++{
++ const struct v4l2_ctrl_ops *ops = &ov4689_ctrl_ops;
++ struct ov4689_ctrls *ctrls = &sensor->ctrls;
++ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++ int ret;
++
++ v4l2_ctrl_handler_init(hdl, 32);
++
++ /* we can use our own mutex for the ctrl lock */
++ hdl->lock = &sensor->lock;
++
++ /* Clock related controls */
++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++ 0, INT_MAX, 1,
++ ov4689_calc_pixel_rate(sensor));
++
++ /* Auto/manual white balance */
++ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, 1, 1, 0);
++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++ 0, 4095, 1, 1024);
++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++ 0, 4095, 1, 1024);
++
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 4, 0xfff8, 1, 0x4c00);
++ ctrls->anal_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
++ 0x10, 0xfff8, 1, 0x0080);
++ ctrls->test_pattern =
++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(test_pattern_menu) - 1,
++ 0, 0, test_pattern_menu);
++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++ 0, 1, 1, 0);
++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++ 0, 1, 1, 0);
++ ctrls->light_freq =
++ v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_POWER_LINE_FREQUENCY,
++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
++ 0, 0, link_freq_menu_items);
++ if (hdl->error) {
++ ret = hdl->error;
++ goto free_ctrls;
++ }
++
++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ // ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++ // ctrls->anal_gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++
++ sensor->sd.ctrl_handler = hdl;
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(hdl);
++ return ret;
++}
++
++static int ov4689_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ if (fse->pad != 0)
++ return -EINVAL;
++ if (fse->index >= OV4689_NUM_MODES)
++ return -EINVAL;
++
++ fse->min_width =
++ ov4689_mode_data[fse->index].hact;
++ fse->max_width = fse->min_width;
++ fse->min_height =
++ ov4689_mode_data[fse->index].vact;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int ov4689_enum_frame_interval(
++ struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_interval_enum *fie)
++{
++ //struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ struct v4l2_fract tpf;
++ int i = 0;
++
++ if (fie->pad != 0)
++ return -EINVAL;
++ if (fie->index >= OV4689_NUM_FRAMERATES)
++ return -EINVAL;
++
++ tpf.numerator = 1;
++ tpf.denominator = ov4689_framerates[fie->index];
++
++ // ret = ov4689_try_frame_interval(sensor, &tpf,
++ // fie->width, fie->height);
++ // if (ret < 0)
++ // return -EINVAL;
++
++ pr_debug("fie->width = %d, fie->height = %d\n", fie->width, fie->height);
++ for (i = 0; i < OV4689_NUM_MODES; i++) {
++ if (fie->width == ov4689_mode_data[i].hact &&
++ fie->height == ov4689_mode_data[i].vact)
++ break;
++ }
++ if (i == OV4689_NUM_MODES)
++ return -ENOTTY;
++
++ fie->interval = tpf;
++ fie->width = ov4689_mode_data[i].hact;
++ fie->height = ov4689_mode_data[i].vact;
++
++ return 0;
++}
++
++static int ov4689_g_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++ mutex_lock(&sensor->lock);
++ fi->interval = sensor->frame_interval;
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov4689_s_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ const struct ov4689_mode_info *mode;
++ int frame_rate, ret = 0;
++
++ if (fi->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ mode = sensor->current_mode;
++
++ frame_rate = ov4689_try_frame_interval(sensor, &fi->interval,
++ mode->hact, mode->vact);
++ if (frame_rate < 0) {
++ /* Always return a valid frame interval value */
++ fi->interval = sensor->frame_interval;
++ goto out;
++ }
++
++ mode = ov4689_find_mode(sensor, frame_rate, mode->hact,
++ mode->vact, true);
++ if (!mode) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (mode != sensor->current_mode ||
++ frame_rate != sensor->current_fr) {
++ sensor->current_fr = frame_rate;
++ sensor->frame_interval = fi->interval;
++ sensor->current_mode = mode;
++ sensor->pending_mode_change = true;
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov4689_calc_pixel_rate(sensor));
++ }
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int ov4689_stream_start(struct ov4689_dev *sensor, int enable)
++{
++ return ov4689_write_reg(sensor, OV4689_REG_STREAM_ON, enable);
++}
++
++static int ov4689_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++ int ret = 0;
++
++ if (enable) {
++ pm_runtime_get_sync(&sensor->i2c_client->dev);
++
++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++ if (ret) {
++ pm_runtime_put_sync(&sensor->i2c_client->dev);
++ return ret;
++ }
++ }
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming == !enable) {
++ if (enable) {
++ ret = ov4689_restore_mode(sensor);
++ if (ret)
++ goto out;
++ }
++
++ if (enable && sensor->pending_mode_change) {
++ ret = ov4689_set_mode(sensor);
++ if (ret)
++ goto out;
++ }
++
++ if (sensor->ep.bus.mipi_csi2.num_data_lanes == 2) {
++ ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_HI, 0x32);
++ ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_LOW, 0x0c);
++ } else if (sensor->ep.bus.mipi_csi2.num_data_lanes == 4) {
++ ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_HI, 0x72);
++ ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_LOW, 0x00);
++ } else {
++ dev_err(&sensor->i2c_client->dev, "Unsupport lane num\n");
++ }
++
++ ret = ov4689_stream_start(sensor, enable);
++ if (ret)
++ goto out;
++ }
++ sensor->streaming += enable ? 1 : -1;
++ WARN_ON(sensor->streaming < 0);
++out:
++ mutex_unlock(&sensor->lock);
++
++ if (!enable || ret)
++ pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_subdev_core_ops ov4689_core_ops = {
++ .log_status = v4l2_ctrl_subdev_log_status,
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov4689_video_ops = {
++ .g_frame_interval = ov4689_g_frame_interval,
++ .s_frame_interval = ov4689_s_frame_interval,
++ .s_stream = ov4689_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
++ .enum_mbus_code = ov4689_enum_mbus_code,
++ .get_fmt = ov4689_get_fmt,
++ .set_fmt = ov4689_set_fmt,
++ .enum_frame_size = ov4689_enum_frame_size,
++ .enum_frame_interval = ov4689_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops ov4689_subdev_ops = {
++ .core = &ov4689_core_ops,
++ .video = &ov4689_video_ops,
++ .pad = &ov4689_pad_ops,
++};
++
++static int ov4689_get_regulators(struct ov4689_dev *sensor)
++{
++ int i;
++
++ for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
++ sensor->supplies[i].supply = ov4689_supply_name[i];
++
++ return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++ OV4689_NUM_SUPPLIES,
++ sensor->supplies);
++}
++
++static int ov4689_check_chip_id(struct ov4689_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret = 0;
++ u16 chip_id;
++
++ ret = ov4689_read_reg16(sensor, OV4689_REG_CHIP_ID, &chip_id);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to read chip identifier\n",
++ __func__);
++ return ret;
++ }
++
++ if (chip_id != OV4689_CHIP_ID) {
++ dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++ __func__, OV4689_CHIP_ID, chip_id);
++ return -ENXIO;
++ }
++ dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, chip_id);
++
++ return 0;
++}
++
++static int ov4689_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct fwnode_handle *endpoint;
++ struct ov4689_dev *sensor;
++ struct v4l2_mbus_framefmt *fmt;
++ u32 rotation;
++ int ret;
++
++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++ if (!sensor)
++ return -ENOMEM;
++
++ sensor->i2c_client = client;
++
++ fmt = &sensor->fmt;
++ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = 1920;
++ fmt->height = 1080;
++ fmt->field = V4L2_FIELD_NONE;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator = ov4689_framerates[OV4689_30_FPS];
++ sensor->current_fr = OV4689_30_FPS;
++ sensor->current_mode =
++ &ov4689_mode_data[OV4689_MODE_1080P_1920_1080];
++ sensor->last_mode = sensor->current_mode;
++
++
++ /* optional indication of physical rotation of sensor */
++ ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++ &rotation);
++ if (!ret) {
++ switch (rotation) {
++ case 180:
++ sensor->upside_down = true;
++ fallthrough;
++ case 0:
++ break;
++ default:
++ dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++ rotation);
++ }
++ }
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++ NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++ fwnode_handle_put(endpoint);
++ if (ret) {
++ dev_err(dev, "Could not parse endpoint\n");
++ return ret;
++ }
++
++ if (sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
++ dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++ return -EINVAL;
++ }
++
++ /* get system clock (xclk) */
++ sensor->xclk = devm_clk_get(dev, "xclk");
++ if (IS_ERR(sensor->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(sensor->xclk);
++ }
++
++ sensor->xclk_freq = clk_get_rate(sensor->xclk);
++ if (sensor->xclk_freq < OV4689_XCLK_MIN ||
++ sensor->xclk_freq > OV4689_XCLK_MAX) {
++ dev_err(dev, "xclk frequency out of range: %d Hz\n",
++ sensor->xclk_freq);
++ return -EINVAL;
++ }
++
++ /* request optional power down pin */
++ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->pwdn_gpio))
++ return PTR_ERR(sensor->pwdn_gpio);
++
++ /* request optional reset pin */
++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->reset_gpio))
++ return PTR_ERR(sensor->reset_gpio);
++
++ v4l2_i2c_subdev_init(&sensor->sd, client, &ov4689_subdev_ops);
++
++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++ if (ret)
++ return ret;
++
++ ret = ov4689_get_regulators(sensor);
++ if (ret)
++ return ret;
++
++ mutex_init(&sensor->lock);
++
++ ret = ov4689_set_power_on(dev);
++ if (ret) {
++ dev_err(dev, "failed to power on\n");
++ goto entity_cleanup;
++ }
++
++ ret = ov4689_check_chip_id(sensor);
++ if (ret)
++ goto error_power_off;
++
++ ret = ov4689_init_controls(sensor);
++ if (ret)
++ goto error_power_off;
++
++ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++ if (ret)
++ goto free_ctrls;
++
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++error_power_off:
++ ov4689_set_power_off(dev);
++entity_cleanup:
++ media_entity_cleanup(&sensor->sd.entity);
++ mutex_destroy(&sensor->lock);
++ return ret;
++}
++
++static void ov4689_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++ v4l2_async_unregister_subdev(&sensor->sd);
++ media_entity_cleanup(&sensor->sd.entity);
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++ mutex_destroy(&sensor->lock);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ ov4689_set_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct i2c_device_id ov4689_id[] = {
++ { "ov4689", 0 },
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, ov4689_id);
++
++static const struct of_device_id ov4689_dt_ids[] = {
++ { .compatible = "ovti,ov4689" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov4689_dt_ids);
++
++static const struct dev_pm_ops ov4689_pm_ops = {
++ SET_RUNTIME_PM_OPS(ov4689_set_power_off, ov4689_set_power_on, NULL)
++};
++
++static struct i2c_driver ov4689_i2c_driver = {
++ .driver = {
++ .name = "ov4689",
++ .of_match_table = ov4689_dt_ids,
++ .pm = &ov4689_pm_ops,
++ },
++ .id_table = ov4689_id,
++ .probe = ov4689_probe,
++ .remove = ov4689_remove,
++};
++
++module_i2c_driver(ov4689_i2c_driver);
++
++MODULE_DESCRIPTION("OV4689 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov5640.c
+@@ -0,0 +1,3227 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
++ * Copyright (C) 2014-2017 Mentor Graphics Inc.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++/* min/typical/max system clock (xclk) frequencies */
++#define OV5640_XCLK_MIN 6000000
++#define OV5640_XCLK_MAX 54000000
++
++#define OV5640_SKIP_FRAMES 4
++
++#define OV5640_CHIP_ID 0x5640
++#define OV5640_DEFAULT_SLAVE_ID 0x3c
++
++#define OV5640_REG_SYS_RESET02 0x3002
++#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
++#define OV5640_REG_SYS_CTRL0 0x3008
++#define OV5640_REG_SYS_CTRL0_SW_PWDN 0x42
++#define OV5640_REG_SYS_CTRL0_SW_PWUP 0x02
++#define OV5640_REG_CHIP_ID 0x300a
++#define OV5640_REG_IO_MIPI_CTRL00 0x300e
++#define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017
++#define OV5640_REG_PAD_OUTPUT_ENABLE02 0x3018
++#define OV5640_REG_PAD_OUTPUT00 0x3019
++#define OV5640_REG_SYSTEM_CONTROL1 0x302e
++#define OV5640_REG_SC_PLL_CTRL0 0x3034
++#define OV5640_REG_SC_PLL_CTRL1 0x3035
++#define OV5640_REG_SC_PLL_CTRL2 0x3036
++#define OV5640_REG_SC_PLL_CTRL3 0x3037
++#define OV5640_REG_SLAVE_ID 0x3100
++#define OV5640_REG_SCCB_SYS_CTRL1 0x3103
++#define OV5640_REG_SYS_ROOT_DIVIDER 0x3108
++#define OV5640_REG_AWB_R_GAIN 0x3400
++#define OV5640_REG_AWB_G_GAIN 0x3402
++#define OV5640_REG_AWB_B_GAIN 0x3404
++#define OV5640_REG_AWB_MANUAL_CTRL 0x3406
++#define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500
++#define OV5640_REG_AEC_PK_EXPOSURE_MED 0x3501
++#define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502
++#define OV5640_REG_AEC_PK_MANUAL 0x3503
++#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
++#define OV5640_REG_AEC_PK_VTS 0x350c
++#define OV5640_REG_TIMING_DVPHO 0x3808
++#define OV5640_REG_TIMING_DVPVO 0x380a
++#define OV5640_REG_TIMING_HTS 0x380c
++#define OV5640_REG_TIMING_VTS 0x380e
++#define OV5640_REG_TIMING_TC_REG20 0x3820
++#define OV5640_REG_TIMING_TC_REG21 0x3821
++#define OV5640_REG_AEC_CTRL00 0x3a00
++#define OV5640_REG_AEC_B50_STEP 0x3a08
++#define OV5640_REG_AEC_B60_STEP 0x3a0a
++#define OV5640_REG_AEC_CTRL0D 0x3a0d
++#define OV5640_REG_AEC_CTRL0E 0x3a0e
++#define OV5640_REG_AEC_CTRL0F 0x3a0f
++#define OV5640_REG_AEC_CTRL10 0x3a10
++#define OV5640_REG_AEC_CTRL11 0x3a11
++#define OV5640_REG_AEC_CTRL1B 0x3a1b
++#define OV5640_REG_AEC_CTRL1E 0x3a1e
++#define OV5640_REG_AEC_CTRL1F 0x3a1f
++#define OV5640_REG_HZ5060_CTRL00 0x3c00
++#define OV5640_REG_HZ5060_CTRL01 0x3c01
++#define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c
++#define OV5640_REG_FRAME_CTRL01 0x4202
++#define OV5640_REG_FORMAT_CONTROL00 0x4300
++#define OV5640_REG_VFIFO_HSIZE 0x4602
++#define OV5640_REG_VFIFO_VSIZE 0x4604
++#define OV5640_REG_JPG_MODE_SELECT 0x4713
++#define OV5640_REG_CCIR656_CTRL00 0x4730
++#define OV5640_REG_POLARITY_CTRL00 0x4740
++#define OV5640_REG_MIPI_CTRL00 0x4800
++#define OV5640_REG_DEBUG_MODE 0x4814
++#define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f
++#define OV5640_REG_PRE_ISP_TEST_SET1 0x503d
++#define OV5640_REG_SDE_CTRL0 0x5580
++#define OV5640_REG_SDE_CTRL1 0x5581
++#define OV5640_REG_SDE_CTRL3 0x5583
++#define OV5640_REG_SDE_CTRL4 0x5584
++#define OV5640_REG_SDE_CTRL5 0x5585
++#define OV5640_REG_AVG_READOUT 0x56a1
++
++enum ov5640_mode_id {
++ OV5640_MODE_QCIF_176_144 = 0,
++ OV5640_MODE_QVGA_320_240,
++ OV5640_MODE_VGA_640_480,
++ OV5640_MODE_NTSC_720_480,
++ OV5640_MODE_PAL_720_576,
++ OV5640_MODE_XGA_1024_768,
++ OV5640_MODE_720P_1280_720,
++ OV5640_MODE_1080P_1920_1080,
++ OV5640_MODE_QSXGA_2592_1944,
++ OV5640_NUM_MODES,
++};
++
++enum ov5640_frame_rate {
++ OV5640_15_FPS = 0,
++ OV5640_30_FPS,
++ OV5640_60_FPS,
++ OV5640_NUM_FRAMERATES,
++};
++
++enum ov5640_format_mux {
++ OV5640_FMT_MUX_YUV422 = 0,
++ OV5640_FMT_MUX_RGB,
++ OV5640_FMT_MUX_DITHER,
++ OV5640_FMT_MUX_RAW_DPC,
++ OV5640_FMT_MUX_SNR_RAW,
++ OV5640_FMT_MUX_RAW_CIP,
++};
++
++struct ov5640_pixfmt {
++ u32 code;
++ u32 colorspace;
++};
++
++static const struct ov5640_pixfmt ov5640_formats[] = {
++ { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
++ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, },
++ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, },
++};
++
++/*
++ * FIXME: remove this when a subdev API becomes available
++ * to set the MIPI CSI-2 virtual channel.
++ */
++static unsigned int virtual_channel;
++module_param(virtual_channel, uint, 0444);
++MODULE_PARM_DESC(virtual_channel,
++ "MIPI CSI-2 virtual channel (0..3), default 0");
++
++static const int ov5640_framerates[] = {
++ [OV5640_15_FPS] = 15,
++ [OV5640_30_FPS] = 30,
++ [OV5640_60_FPS] = 60,
++};
++
++/* regulator supplies */
++static const char * const ov5640_supply_name[] = {
++ "DOVDD", /* Digital I/O (1.8V) supply */
++ "AVDD", /* Analog (2.8V) supply */
++ "DVDD", /* Digital Core (1.5V) supply */
++};
++
++#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov5640_downsize_mode {
++ SUBSAMPLING,
++ SCALING,
++};
++
++struct reg_value {
++ u16 reg_addr;
++ u8 val;
++ u8 mask;
++ u32 delay_ms;
++};
++
++struct ov5640_mode_info {
++ enum ov5640_mode_id id;
++ enum ov5640_downsize_mode dn_mode;
++ u32 hact;
++ u32 htot;
++ u32 vact;
++ u32 vtot;
++ const struct reg_value *reg_data;
++ u32 reg_data_size;
++ u32 max_fps;
++};
++
++struct ov5640_ctrls {
++ struct v4l2_ctrl_handler handler;
++ struct v4l2_ctrl *pixel_rate;
++ struct {
++ struct v4l2_ctrl *auto_exp;
++ struct v4l2_ctrl *exposure;
++ };
++ struct {
++ struct v4l2_ctrl *auto_wb;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *red_balance;
++ };
++ struct {
++ struct v4l2_ctrl *auto_gain;
++ struct v4l2_ctrl *gain;
++ };
++ struct v4l2_ctrl *brightness;
++ struct v4l2_ctrl *light_freq;
++ struct v4l2_ctrl *saturation;
++ struct v4l2_ctrl *contrast;
++ struct v4l2_ctrl *hue;
++ struct v4l2_ctrl *test_pattern;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vflip;
++};
++
++struct ov5640_dev {
++ struct i2c_client *i2c_client;
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++ struct clk *xclk; /* system clock to OV5640 */
++ u32 xclk_freq;
++
++ struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
++ struct gpio_desc *reset_gpio;
++ struct gpio_desc *pwdn_gpio;
++ bool upside_down;
++
++ /* lock to protect all members below */
++ struct mutex lock;
++
++ int power_count;
++
++ struct v4l2_mbus_framefmt fmt;
++ bool pending_fmt_change;
++
++ const struct ov5640_mode_info *current_mode;
++ const struct ov5640_mode_info *last_mode;
++ enum ov5640_frame_rate current_fr;
++ struct v4l2_fract frame_interval;
++
++ struct ov5640_ctrls ctrls;
++
++ u32 prev_sysclk, prev_hts;
++ u32 ae_low, ae_high, ae_target;
++
++ bool pending_mode_change;
++ int streaming;
++};
++
++static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ov5640_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct ov5640_dev,
++ ctrls.handler)->sd;
++}
++
++/*
++ * FIXME: all of these register tables are likely filled with
++ * entries that set the register to their power-on default values,
++ * and which are otherwise not touched by this driver. Those entries
++ * should be identified and removed to speed register load time
++ * over i2c.
++ */
++/* YUV422 UYVY VGA@30fps */
++static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
++ {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
++ {0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
++ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
++ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
++ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
++ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
++ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
++ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
++ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
++ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
++ {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
++ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
++ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
++ {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
++ {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0},
++ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0},
++ {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
++ {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
++ {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
++ {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
++ {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
++ {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
++ {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
++ {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
++ {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
++ {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
++ {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
++ {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
++ {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
++ {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
++ {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
++ {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
++ {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
++ {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
++ {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
++ {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
++ {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
++ {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
++ {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
++ {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
++ {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
++ {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
++ {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
++ {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
++ {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
++ {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
++ {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
++ {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
++ {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
++ {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
++ {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
++ {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
++ {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
++ {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
++ {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
++ {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
++ {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
++ {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
++ {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
++ {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
++ {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
++ {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
++ {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
++ {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
++ {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
++ {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
++};
++
++static const struct reg_value ov5640_setting_VGA_640_480[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_XGA_1024_768[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QVGA_320_240[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QCIF_176_144[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_NTSC_720_480[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_PAL_720_576[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_720P_1280_720[] = {
++ {0x3c07, 0x07, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x31, 0, 0},
++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
++ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
++ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
++ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
++ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x11, 0, 0},
++ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
++ {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
++ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
++ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
++ {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
++ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
++ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
++ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
++ {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
++ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
++ {0x4005, 0x1a, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
++ {0x3c07, 0x08, 0, 0},
++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++ {0x3814, 0x11, 0, 0},
++ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
++ {0x3810, 0x00, 0, 0},
++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
++ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 70},
++};
++
++/* power-on sensor init reg table */
++static const struct ov5640_mode_info ov5640_mode_init_data = {
++ 0, SUBSAMPLING, 640, 1896, 480, 984,
++ ov5640_init_setting_30fps_VGA,
++ ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
++ OV5640_30_FPS,
++};
++
++static const struct ov5640_mode_info
++ov5640_mode_data[OV5640_NUM_MODES] = {
++ {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
++ 176, 1896, 144, 984,
++ ov5640_setting_QCIF_176_144,
++ ARRAY_SIZE(ov5640_setting_QCIF_176_144),
++ OV5640_30_FPS},
++ {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
++ 320, 1896, 240, 984,
++ ov5640_setting_QVGA_320_240,
++ ARRAY_SIZE(ov5640_setting_QVGA_320_240),
++ OV5640_30_FPS},
++ {OV5640_MODE_VGA_640_480, SUBSAMPLING,
++ 640, 1896, 480, 1080,
++ ov5640_setting_VGA_640_480,
++ ARRAY_SIZE(ov5640_setting_VGA_640_480),
++ OV5640_60_FPS},
++ {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
++ 720, 1896, 480, 984,
++ ov5640_setting_NTSC_720_480,
++ ARRAY_SIZE(ov5640_setting_NTSC_720_480),
++ OV5640_30_FPS},
++ {OV5640_MODE_PAL_720_576, SUBSAMPLING,
++ 720, 1896, 576, 984,
++ ov5640_setting_PAL_720_576,
++ ARRAY_SIZE(ov5640_setting_PAL_720_576),
++ OV5640_30_FPS},
++ {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
++ 1024, 1896, 768, 1080,
++ ov5640_setting_XGA_1024_768,
++ ARRAY_SIZE(ov5640_setting_XGA_1024_768),
++ OV5640_30_FPS},
++ {OV5640_MODE_720P_1280_720, SUBSAMPLING,
++ 1280, 1892, 720, 740,
++ ov5640_setting_720P_1280_720,
++ ARRAY_SIZE(ov5640_setting_720P_1280_720),
++ OV5640_30_FPS},
++ {OV5640_MODE_1080P_1920_1080, SCALING,
++ 1920, 2500, 1080, 1120,
++ ov5640_setting_1080P_1920_1080,
++ ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
++ OV5640_30_FPS},
++ {OV5640_MODE_QSXGA_2592_1944, SCALING,
++ 2592, 2844, 1944, 1968,
++ ov5640_setting_QSXGA_2592_1944,
++ ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
++ OV5640_15_FPS},
++};
++
++static int ov5640_init_slave_id(struct ov5640_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg;
++ u8 buf[3];
++ int ret;
++
++ if (client->addr == OV5640_DEFAULT_SLAVE_ID)
++ return 0;
++
++ buf[0] = OV5640_REG_SLAVE_ID >> 8;
++ buf[1] = OV5640_REG_SLAVE_ID & 0xff;
++ buf[2] = client->addr << 1;
++
++ msg.addr = OV5640_DEFAULT_SLAVE_ID;
++ msg.flags = 0;
++ msg.buf = buf;
++ msg.len = sizeof(buf);
++
++ ret = i2c_transfer(client->adapter, &msg, 1);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg;
++ u8 buf[3];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val;
++
++ msg.addr = client->addr;
++ msg.flags = client->flags;
++ msg.buf = buf;
++ msg.len = sizeof(buf);
++
++ ret = i2c_transfer(client->adapter, &msg, 1);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++ __func__, reg, val);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg[2];
++ u8 buf[2];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++
++ msg[0].addr = client->addr;
++ msg[0].flags = client->flags;
++ msg[0].buf = buf;
++ msg[0].len = sizeof(buf);
++
++ msg[1].addr = client->addr;
++ msg[1].flags = client->flags | I2C_M_RD;
++ msg[1].buf = buf;
++ msg[1].len = 1;
++
++ ret = i2c_transfer(client->adapter, msg, 2);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x\n",
++ __func__, reg);
++ return ret;
++ }
++
++ *val = buf[0];
++ return 0;
++}
++
++static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
++{
++ u8 hi, lo;
++ int ret;
++
++ ret = ov5640_read_reg(sensor, reg, &hi);
++ if (ret)
++ return ret;
++ ret = ov5640_read_reg(sensor, reg + 1, &lo);
++ if (ret)
++ return ret;
++
++ *val = ((u16)hi << 8) | (u16)lo;
++ return 0;
++}
++
++static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
++{
++ int ret;
++
++ ret = ov5640_write_reg(sensor, reg, val >> 8);
++ if (ret)
++ return ret;
++
++ return ov5640_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
++ u8 mask, u8 val)
++{
++ u8 readval;
++ int ret;
++
++ ret = ov5640_read_reg(sensor, reg, &readval);
++ if (ret)
++ return ret;
++
++ readval &= ~mask;
++ val &= mask;
++ val |= readval;
++
++ return ov5640_write_reg(sensor, reg, val);
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ * +--------------+
++ * | Ext. Clock |
++ * +-+------------+
++ * | +----------+
++ * +->| PLL1 | - reg 0x3036, for the multiplier
++ * +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
++ * | +--------------+
++ * +->| System Clock | - reg 0x3035, bits 4-7
++ * +-+------------+
++ * | +--------------+
++ * +->| MIPI Divider | - reg 0x3035, bits 0-3
++ * | +-+------------+
++ * | +----------------> MIPI SCLK
++ * | + +-----+
++ * | +->| / 2 |-------> MIPI BIT CLK
++ * | +-----+
++ * | +--------------+
++ * +->| PLL Root Div | - reg 0x3037, bit 4
++ * +-+------------+
++ * | +---------+
++ * +->| Bit Div | - reg 0x3034, bits 0-3
++ * +-+-------+
++ * | +-------------+
++ * +->| SCLK Div | - reg 0x3108, bits 0-1
++ * | +-+-----------+
++ * | +---------------> SCLK
++ * | +-------------+
++ * +->| SCLK 2X Div | - reg 0x3108, bits 2-3
++ * | +-+-----------+
++ * | +---------------> SCLK 2X
++ * | +-------------+
++ * +->| PCLK Div | - reg 0x3108, bits 4-5
++ * ++------------+
++ * + +-----------+
++ * +->| P_DIV | - reg 0x3035, bits 0-3
++ * +-----+-----+
++ * +------------> PCLK
++ *
++ * This is deviating from the datasheet at least for the register
++ * 0x3108, since it's said here that the PCLK would be clocked from
++ * the PLL.
++ *
++ * There seems to be also (unverified) constraints:
++ * - the PLL pre-divider output rate should be in the 4-27MHz range
++ * - the PLL multiplier output rate should be in the 500-1000MHz range
++ * - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
++ *
++ * In the two latter cases, these constraints are met since our
++ * factors are hardcoded. If we were to change that, we would need to
++ * take this into account. The only varying parts are the PLL
++ * multiplier and the system clock divider, which are shared between
++ * all these clocks so won't cause any issue.
++ */
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 3 in the vendor kernels.
++ */
++#define OV5640_PLL_PREDIV 3
++
++#define OV5640_PLL_MULT_MIN 4
++#define OV5640_PLL_MULT_MAX 252
++
++/*
++ * This is supposed to be ranging from 1 to 16, but the value is
++ * always set to either 1 or 2 in the vendor kernels.
++ */
++#define OV5640_SYSDIV_MIN 1
++#define OV5640_SYSDIV_MAX 16
++
++/*
++ * Hardcode these values for scaler and non-scaler modes.
++ * FIXME: to be re-calcualted for 1 data lanes setups
++ */
++#define OV5640_MIPI_DIV_PCLK 2
++#define OV5640_MIPI_DIV_SCLK 1
++
++/*
++ * This is supposed to be ranging from 1 to 2, but the value is always
++ * set to 2 in the vendor kernels.
++ */
++#define OV5640_PLL_ROOT_DIV 2
++#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 BIT(4)
++
++/*
++ * We only supports 8-bit formats at the moment
++ */
++#define OV5640_BIT_DIV 2
++#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT 0x08
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 2 in the vendor kernels.
++ */
++#define OV5640_SCLK_ROOT_DIV 2
++
++/*
++ * This is hardcoded so that the consistency is maintained between SCLK and
++ * SCLK 2x.
++ */
++#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 1 in the vendor kernels.
++ */
++#define OV5640_PCLK_ROOT_DIV 1
++#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS 0x00
++
++static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
++ u8 pll_prediv, u8 pll_mult,
++ u8 sysdiv)
++{
++ unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult;
++
++ /* PLL1 output cannot exceed 1GHz. */
++ if (sysclk / 1000000 > 1000)
++ return 0;
++
++ return sysclk / sysdiv;
++}
++
++static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
++ unsigned long rate,
++ u8 *pll_prediv, u8 *pll_mult,
++ u8 *sysdiv)
++{
++ unsigned long best = ~0;
++ u8 best_sysdiv = 1, best_mult = 1;
++ u8 _sysdiv, _pll_mult;
++
++ for (_sysdiv = OV5640_SYSDIV_MIN;
++ _sysdiv <= OV5640_SYSDIV_MAX;
++ _sysdiv++) {
++ for (_pll_mult = OV5640_PLL_MULT_MIN;
++ _pll_mult <= OV5640_PLL_MULT_MAX;
++ _pll_mult++) {
++ unsigned long _rate;
++
++ /*
++ * The PLL multiplier cannot be odd if above
++ * 127.
++ */
++ if (_pll_mult > 127 && (_pll_mult % 2))
++ continue;
++
++ _rate = ov5640_compute_sys_clk(sensor,
++ OV5640_PLL_PREDIV,
++ _pll_mult, _sysdiv);
++
++ /*
++ * We have reached the maximum allowed PLL1 output,
++ * increase sysdiv.
++ */
++ if (!_rate)
++ break;
++
++ /*
++ * Prefer rates above the expected clock rate than
++ * below, even if that means being less precise.
++ */
++ if (_rate < rate)
++ continue;
++
++ if (abs(rate - _rate) < abs(rate - best)) {
++ best = _rate;
++ best_sysdiv = _sysdiv;
++ best_mult = _pll_mult;
++ }
++
++ if (_rate == rate)
++ goto out;
++ }
++ }
++
++out:
++ *sysdiv = best_sysdiv;
++ *pll_prediv = OV5640_PLL_PREDIV;
++ *pll_mult = best_mult;
++
++ return best;
++}
++
++/*
++ * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
++ * for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ * 'Bandwidth Per Lane' is calculated as:
++ * bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ * - sample_rate = bpl / (bpp / num_lanes);
++ * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
++ *
++ * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
++ *
++ * with these fixed parameters:
++ * PLL_RDIV = 2;
++ * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ * PCLK_DIV = 1;
++ *
++ * The MIPI clock generation differs for modes that use the scaler and modes
++ * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
++ * BIT CLk, and thus:
++ *
++ * - mipi_sclk = bpl / MIPI_DIV / 2;
++ * MIPI_DIV = 1;
++ *
++ * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
++ * from the pixel clock, and thus:
++ *
++ * - sample_rate = bpl / (bpp / num_lanes);
++ * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
++ * = bpl / (4 * MIPI_DIV / num_lanes);
++ * - MIPI_DIV = bpp / (4 * num_lanes);
++ *
++ * FIXME: this have been tested with 16bpp and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
++ unsigned long rate)
++{
++ const struct ov5640_mode_info *mode = sensor->current_mode;
++ u8 prediv, mult, sysdiv;
++ u8 mipi_div;
++ int ret;
++
++ /*
++ * 1280x720 is reported to use 'SUBSAMPLING' only,
++ * but according to the sensor manual it goes through the
++ * scaler before subsampling.
++ */
++ if (mode->dn_mode == SCALING ||
++ (mode->id == OV5640_MODE_720P_1280_720))
++ mipi_div = OV5640_MIPI_DIV_SCLK;
++ else
++ mipi_div = OV5640_MIPI_DIV_PCLK;
++
++ ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
++ 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
++ 0xff, sysdiv << 4 | mipi_div);
++ if (ret)
++ return ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
++ if (ret)
++ return ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
++ 0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
++ if (ret)
++ return ret;
++
++ return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
++ 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
++}
++
++static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
++ unsigned long rate,
++ u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
++ u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
++{
++ unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
++ OV5640_PCLK_ROOT_DIV;
++
++ _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
++ sysdiv);
++ *pll_rdiv = OV5640_PLL_ROOT_DIV;
++ *bit_div = OV5640_BIT_DIV;
++ *pclk_div = OV5640_PCLK_ROOT_DIV;
++
++ return _rate / *pll_rdiv / *bit_div / *pclk_div;
++}
++
++static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
++{
++ u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
++ int ret;
++
++ ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
++ &bit_div, &pclk_div);
++
++#ifndef CONFIG_VIN_SENSOR_OV5640
++ if (bit_div == 2)
++ bit_div = 8;
++#else
++ bit_div = 0xa;
++#endif
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
++ 0x0f, bit_div);
++ if (ret)
++ return ret;
++
++ /*
++ * We need to set sysdiv according to the clock, and to clear
++ * the MIPI divider.
++ */
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
++ 0xff, sysdiv << 4);
++ if (ret)
++ return ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
++ 0xff, mult);
++ if (ret)
++ return ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
++ 0x1f, prediv | ((pll_rdiv - 1) << 4));
++ if (ret)
++ return ret;
++
++ return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
++ (ilog2(pclk_div) << 4));
++}
++
++/* set JPEG framing sizes */
++static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
++ const struct ov5640_mode_info *mode)
++{
++ int ret;
++
++ /*
++ * compression mode 3 timing
++ *
++ * Data is transmitted with programmable width (VFIFO_HSIZE).
++ * No padding done. Last line may have less data. Varying
++ * number of lines per frame, depending on amount of data.
++ */
++ ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3);
++ if (ret < 0)
++ return ret;
++
++ ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact);
++ if (ret < 0)
++ return ret;
++
++ return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
++}
++
++/* download ov5640 settings to sensor through i2c */
++static int ov5640_set_timings(struct ov5640_dev *sensor,
++ const struct ov5640_mode_info *mode)
++{
++ int ret;
++
++ if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
++ ret = ov5640_set_jpeg_timings(sensor, mode);
++ if (ret < 0)
++ return ret;
++ }
++
++ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
++ if (ret < 0)
++ return ret;
++
++ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
++ if (ret < 0)
++ return ret;
++
++ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
++ if (ret < 0)
++ return ret;
++
++ return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
++}
++
++static int ov5640_load_regs(struct ov5640_dev *sensor,
++ const struct ov5640_mode_info *mode)
++{
++ const struct reg_value *regs = mode->reg_data;
++ unsigned int i;
++ u32 delay_ms;
++ u16 reg_addr;
++ u8 mask, val;
++ int ret = 0;
++
++ st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++ for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++ delay_ms = regs->delay_ms;
++ reg_addr = regs->reg_addr;
++ val = regs->val;
++ mask = regs->mask;
++
++ /* remain in power down mode for DVP */
++ if (regs->reg_addr == OV5640_REG_SYS_CTRL0 &&
++ val == OV5640_REG_SYS_CTRL0_SW_PWUP &&
++ sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY)
++ continue;
++
++ if (mask)
++ ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
++ else
++ ret = ov5640_write_reg(sensor, reg_addr, val);
++ if (ret)
++ break;
++
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++
++ return ov5640_set_timings(sensor, mode);
++}
++
++static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
++{
++ return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
++ BIT(0), on ? 0 : BIT(0));
++}
++
++/* read exposure, in number of line periods */
++static int ov5640_get_exposure(struct ov5640_dev *sensor)
++{
++ int exp, ret;
++ u8 temp;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
++ if (ret)
++ return ret;
++ exp = ((int)temp & 0x0f) << 16;
++ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
++ if (ret)
++ return ret;
++ exp |= ((int)temp << 8);
++ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
++ if (ret)
++ return ret;
++ exp |= (int)temp;
++
++ return exp >> 4;
++}
++
++/* write exposure, given number of line periods */
++static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
++{
++ int ret;
++
++ exposure <<= 4;
++
++ ret = ov5640_write_reg(sensor,
++ OV5640_REG_AEC_PK_EXPOSURE_LO,
++ exposure & 0xff);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor,
++ OV5640_REG_AEC_PK_EXPOSURE_MED,
++ (exposure >> 8) & 0xff);
++ if (ret)
++ return ret;
++ return ov5640_write_reg(sensor,
++ OV5640_REG_AEC_PK_EXPOSURE_HI,
++ (exposure >> 16) & 0x0f);
++}
++
++static int ov5640_get_gain(struct ov5640_dev *sensor)
++{
++ u16 gain;
++ int ret;
++
++ ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
++ if (ret)
++ return ret;
++
++ return gain & 0x3ff;
++}
++
++static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
++{
++ return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
++ (u16)gain & 0x3ff);
++}
++
++static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
++{
++ return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
++ BIT(1), on ? 0 : BIT(1));
++}
++
++static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
++{
++ return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ?
++ OV5640_REG_SYS_CTRL0_SW_PWUP :
++ OV5640_REG_SYS_CTRL0_SW_PWDN);
++}
++
++static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
++{
++ int ret;
++
++ /*
++ * Enable/disable the MIPI interface
++ *
++ * 0x300e = on ? 0x45 : 0x40
++ *
++ * FIXME: the sensor manual (version 2.03) reports
++ * [7:5] = 000 : 1 data lane mode
++ * [7:5] = 001 : 2 data lanes mode
++ * But this settings do not work, while the following ones
++ * have been validated for 2 data lanes mode.
++ *
++ * [7:5] = 010 : 2 data lanes mode
++ * [4] = 0 : Power up MIPI HS Tx
++ * [3] = 0 : Power up MIPI LS Rx
++ * [2] = 1/0 : MIPI interface enable/disable
++ * [1:0] = 01/00: FIXME: 'debug'
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00,
++ on ? 0x45 : 0x40);
++ if (ret)
++ return ret;
++
++ return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
++ on ? 0x00 : 0x0f);
++}
++
++static int ov5640_get_sysclk(struct ov5640_dev *sensor)
++{
++ /* calculate sysclk */
++ u32 xvclk = sensor->xclk_freq / 10000;
++ u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
++ u32 sclk_rdiv_map[] = {1, 2, 4, 8};
++ u32 bit_div2x = 1, sclk_rdiv, sysclk;
++ u8 temp1, temp2;
++ int ret;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
++ if (ret)
++ return ret;
++ temp2 = temp1 & 0x0f;
++ if (temp2 == 8 || temp2 == 10)
++ bit_div2x = temp2 / 2;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
++ if (ret)
++ return ret;
++ sysdiv = temp1 >> 4;
++ if (sysdiv == 0)
++ sysdiv = 16;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
++ if (ret)
++ return ret;
++ multiplier = temp1;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
++ if (ret)
++ return ret;
++ prediv = temp1 & 0x0f;
++ pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
++ if (ret)
++ return ret;
++ temp2 = temp1 & 0x03;
++ sclk_rdiv = sclk_rdiv_map[temp2];
++
++ if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
++ return -EINVAL;
++
++ VCO = xvclk * multiplier / prediv;
++
++ sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
++
++ return sysclk;
++}
++
++static int ov5640_set_night_mode(struct ov5640_dev *sensor)
++{
++ /* read HTS from register settings */
++ u8 mode;
++ int ret;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
++ if (ret)
++ return ret;
++ mode &= 0xfb;
++ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
++}
++
++static int ov5640_get_hts(struct ov5640_dev *sensor)
++{
++ /* read HTS from register settings */
++ u16 hts;
++ int ret;
++
++ ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
++ if (ret)
++ return ret;
++ return hts;
++}
++
++static int ov5640_get_vts(struct ov5640_dev *sensor)
++{
++ u16 vts;
++ int ret;
++
++ ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
++ if (ret)
++ return ret;
++ return vts;
++}
++
++static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
++{
++ return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
++}
++
++static int ov5640_get_light_freq(struct ov5640_dev *sensor)
++{
++ /* get banding filter value */
++ int ret, light_freq = 0;
++ u8 temp, temp1;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
++ if (ret)
++ return ret;
++
++ if (temp & 0x80) {
++ /* manual */
++ ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
++ &temp1);
++ if (ret)
++ return ret;
++ if (temp1 & 0x04) {
++ /* 50Hz */
++ light_freq = 50;
++ } else {
++ /* 60Hz */
++ light_freq = 60;
++ }
++ } else {
++ /* auto */
++ ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
++ &temp1);
++ if (ret)
++ return ret;
++
++ if (temp1 & 0x01) {
++ /* 50Hz */
++ light_freq = 50;
++ } else {
++ /* 60Hz */
++ }
++ }
++
++ return light_freq;
++}
++
++static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
++{
++ u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
++ int ret;
++
++ /* read preview PCLK */
++ ret = ov5640_get_sysclk(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ return -EINVAL;
++ sensor->prev_sysclk = ret;
++ /* read preview HTS */
++ ret = ov5640_get_hts(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ return -EINVAL;
++ sensor->prev_hts = ret;
++
++ /* read preview VTS */
++ ret = ov5640_get_vts(sensor);
++ if (ret < 0)
++ return ret;
++ prev_vts = ret;
++
++ /* calculate banding filter */
++ /* 60Hz */
++ band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
++ ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
++ if (ret)
++ return ret;
++ if (!band_step60)
++ return -EINVAL;
++ max_band60 = (int)((prev_vts - 4) / band_step60);
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
++ if (ret)
++ return ret;
++
++ /* 50Hz */
++ band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
++ ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
++ if (ret)
++ return ret;
++ if (!band_step50)
++ return -EINVAL;
++ max_band50 = (int)((prev_vts - 4) / band_step50);
++ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
++}
++
++static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
++{
++ /* stable in high */
++ u32 fast_high, fast_low;
++ int ret;
++
++ sensor->ae_low = target * 23 / 25; /* 0.92 */
++ sensor->ae_high = target * 27 / 25; /* 1.08 */
++
++ fast_high = sensor->ae_high << 1;
++ if (fast_high > 255)
++ fast_high = 255;
++
++ fast_low = sensor->ae_low >> 1;
++
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
++ if (ret)
++ return ret;
++ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
++}
++
++static int ov5640_get_binning(struct ov5640_dev *sensor)
++{
++ u8 temp;
++ int ret;
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
++ if (ret)
++ return ret;
++
++ return temp & BIT(0);
++}
++
++static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
++{
++ int ret;
++
++ /*
++ * TIMING TC REG21:
++ * - [0]: Horizontal binning enable
++ */
++ ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++ BIT(0), enable ? BIT(0) : 0);
++ if (ret)
++ return ret;
++ /*
++ * TIMING TC REG20:
++ * - [0]: Undocumented, but hardcoded init sequences
++ * are always setting REG21/REG20 bit 0 to same value...
++ */
++ return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
++ BIT(0), enable ? BIT(0) : 0);
++}
++
++static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ u8 temp, channel = virtual_channel;
++ int ret;
++
++ if (channel > 3) {
++ dev_err(&client->dev,
++ "%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
++ __func__, channel);
++ return -EINVAL;
++ }
++
++ ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
++ if (ret)
++ return ret;
++ temp &= ~(3 << 6);
++ temp |= (channel << 6);
++ return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
++}
++
++static const struct ov5640_mode_info *
++ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
++ int width, int height, bool nearest)
++{
++ const struct ov5640_mode_info *mode;
++
++ mode = v4l2_find_nearest_size(ov5640_mode_data,
++ ARRAY_SIZE(ov5640_mode_data),
++ hact, vact,
++ width, height);
++
++ if (!mode ||
++ (!nearest && (mode->hact != width || mode->vact != height)))
++ return NULL;
++
++ /* Check to see if the current mode exceeds the max frame rate */
++ if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps])
++ return NULL;
++
++ return mode;
++}
++
++static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
++{
++ u64 rate;
++
++ rate = sensor->current_mode->vtot * sensor->current_mode->htot;
++ rate *= ov5640_framerates[sensor->current_fr];
++
++ return rate;
++}
++
++/*
++ * sensor changes between scaling and subsampling, go through
++ * exposure calculation
++ */
++static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
++ const struct ov5640_mode_info *mode)
++{
++ u32 prev_shutter, prev_gain16;
++ u32 cap_shutter, cap_gain16;
++ u32 cap_sysclk, cap_hts, cap_vts;
++ u32 light_freq, cap_bandfilt, cap_maxband;
++ u32 cap_gain16_shutter;
++ u8 average;
++ int ret;
++
++ if (!mode->reg_data)
++ return -EINVAL;
++
++ /* read preview shutter */
++ ret = ov5640_get_exposure(sensor);
++ if (ret < 0)
++ return ret;
++ prev_shutter = ret;
++ ret = ov5640_get_binning(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
++ mode->id != OV5640_MODE_1080P_1920_1080)
++ prev_shutter *= 2;
++
++ /* read preview gain */
++ ret = ov5640_get_gain(sensor);
++ if (ret < 0)
++ return ret;
++ prev_gain16 = ret;
++
++ /* get average */
++ ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
++ if (ret)
++ return ret;
++
++ /* turn off night mode for capture */
++ ret = ov5640_set_night_mode(sensor);
++ if (ret < 0)
++ return ret;
++
++ /* Write capture setting */
++ ret = ov5640_load_regs(sensor, mode);
++ if (ret < 0)
++ return ret;
++
++ /* read capture VTS */
++ ret = ov5640_get_vts(sensor);
++ if (ret < 0)
++ return ret;
++ cap_vts = ret;
++ ret = ov5640_get_hts(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ return -EINVAL;
++ cap_hts = ret;
++
++ ret = ov5640_get_sysclk(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ return -EINVAL;
++ cap_sysclk = ret;
++
++ /* calculate capture banding filter */
++ ret = ov5640_get_light_freq(sensor);
++ if (ret < 0)
++ return ret;
++ light_freq = ret;
++
++ if (light_freq == 60) {
++ /* 60Hz */
++ cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
++ } else {
++ /* 50Hz */
++ cap_bandfilt = cap_sysclk * 100 / cap_hts;
++ }
++
++ if (!sensor->prev_sysclk) {
++ ret = ov5640_get_sysclk(sensor);
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ return -EINVAL;
++ sensor->prev_sysclk = ret;
++ }
++
++ if (!cap_bandfilt)
++ return -EINVAL;
++
++ cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
++
++ /* calculate capture shutter/gain16 */
++ if (average > sensor->ae_low && average < sensor->ae_high) {
++ /* in stable range */
++ cap_gain16_shutter =
++ prev_gain16 * prev_shutter *
++ cap_sysclk / sensor->prev_sysclk *
++ sensor->prev_hts / cap_hts *
++ sensor->ae_target / average;
++ } else {
++ cap_gain16_shutter =
++ prev_gain16 * prev_shutter *
++ cap_sysclk / sensor->prev_sysclk *
++ sensor->prev_hts / cap_hts;
++ }
++
++ /* gain to shutter */
++ if (cap_gain16_shutter < (cap_bandfilt * 16)) {
++ /* shutter < 1/100 */
++ cap_shutter = cap_gain16_shutter / 16;
++ if (cap_shutter < 1)
++ cap_shutter = 1;
++
++ cap_gain16 = cap_gain16_shutter / cap_shutter;
++ if (cap_gain16 < 16)
++ cap_gain16 = 16;
++ } else {
++ if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
++ /* exposure reach max */
++ cap_shutter = cap_bandfilt * cap_maxband;
++ if (!cap_shutter)
++ return -EINVAL;
++
++ cap_gain16 = cap_gain16_shutter / cap_shutter;
++ } else {
++ /* 1/100 < (cap_shutter = n/100) =< max */
++ cap_shutter =
++ ((int)(cap_gain16_shutter / 16 / cap_bandfilt))
++ * cap_bandfilt;
++ if (!cap_shutter)
++ return -EINVAL;
++
++ cap_gain16 = cap_gain16_shutter / cap_shutter;
++ }
++ }
++
++ /* set capture gain */
++ ret = ov5640_set_gain(sensor, cap_gain16);
++ if (ret)
++ return ret;
++
++ /* write capture shutter */
++ if (cap_shutter > (cap_vts - 4)) {
++ cap_vts = cap_shutter + 4;
++ ret = ov5640_set_vts(sensor, cap_vts);
++ if (ret < 0)
++ return ret;
++ }
++
++ /* set exposure */
++ return ov5640_set_exposure(sensor, cap_shutter);
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
++ const struct ov5640_mode_info *mode)
++{
++ if (!mode->reg_data)
++ return -EINVAL;
++
++ /* Write capture setting */
++ return ov5640_load_regs(sensor, mode);
++}
++
++static int ov5640_set_mode(struct ov5640_dev *sensor)
++{
++ const struct ov5640_mode_info *mode = sensor->current_mode;
++ const struct ov5640_mode_info *orig_mode = sensor->last_mode;
++ enum ov5640_downsize_mode dn_mode, orig_dn_mode;
++ bool auto_gain = sensor->ctrls.auto_gain->val == 1;
++ bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
++ unsigned long rate;
++ int ret;
++
++ dn_mode = mode->dn_mode;
++ orig_dn_mode = orig_mode->dn_mode;
++
++ /* auto gain and exposure must be turned off when changing modes */
++ if (auto_gain) {
++ ret = ov5640_set_autogain(sensor, false);
++ if (ret)
++ return ret;
++ }
++
++ if (auto_exp) {
++ ret = ov5640_set_autoexposure(sensor, false);
++ if (ret)
++ goto restore_auto_gain;
++ }
++
++ /*
++ * All the formats we support have 16 bits per pixel, seems to require
++ * the same rate than YUV, so we can just use 16 bpp all the time.
++ */
++ rate = ov5640_calc_pixel_rate(sensor) * 16;
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
++ rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
++ ret = ov5640_set_mipi_pclk(sensor, rate);
++ } else {
++ rate = rate / sensor->ep.bus.parallel.bus_width;
++ ret = ov5640_set_dvp_pclk(sensor, rate);
++ }
++
++ if (ret < 0)
++ return 0;
++
++ if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
++ (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
++ /*
++ * change between subsampling and scaling
++ * go through exposure calculation
++ */
++ ret = ov5640_set_mode_exposure_calc(sensor, mode);
++ } else {
++ /*
++ * change inside subsampling or scaling
++ * download firmware directly
++ */
++ ret = ov5640_set_mode_direct(sensor, mode);
++ }
++ if (ret < 0)
++ goto restore_auto_exp_gain;
++
++ /* restore auto gain and exposure */
++ if (auto_gain)
++ ov5640_set_autogain(sensor, true);
++ if (auto_exp)
++ ov5640_set_autoexposure(sensor, true);
++
++ ret = ov5640_set_binning(sensor, dn_mode != SCALING);
++ if (ret < 0)
++ return ret;
++ ret = ov5640_set_ae_target(sensor, sensor->ae_target);
++ if (ret < 0)
++ return ret;
++ ret = ov5640_get_light_freq(sensor);
++ if (ret < 0)
++ return ret;
++ ret = ov5640_set_bandingfilter(sensor);
++ if (ret < 0)
++ return ret;
++ ret = ov5640_set_virtual_channel(sensor);
++ if (ret < 0)
++ return ret;
++
++ sensor->pending_mode_change = false;
++ sensor->last_mode = mode;
++
++ return 0;
++
++restore_auto_exp_gain:
++ if (auto_exp)
++ ov5640_set_autoexposure(sensor, true);
++restore_auto_gain:
++ if (auto_gain)
++ ov5640_set_autogain(sensor, true);
++
++ return ret;
++}
++
++static int ov5640_set_framefmt(struct ov5640_dev *sensor,
++ struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int ov5640_restore_mode(struct ov5640_dev *sensor)
++{
++ int ret;
++
++ /* first load the initial register values */
++ ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
++ if (ret < 0)
++ return ret;
++ sensor->last_mode = &ov5640_mode_init_data;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
++ (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
++ ilog2(OV5640_SCLK_ROOT_DIV));
++ if (ret)
++ return ret;
++
++ /* now restore the last capture mode */
++ ret = ov5640_set_mode(sensor);
++ if (ret < 0)
++ return ret;
++
++ return ov5640_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void ov5640_power(struct ov5640_dev *sensor, bool enable)
++{
++ if (!sensor->pwdn_gpio)
++ return;
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void ov5640_reset(struct ov5640_dev *sensor)
++{
++ if (!sensor->reset_gpio)
++ return;
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++ /* camera power cycle */
++ ov5640_power(sensor, false);
++ usleep_range(5000, 10000);
++ ov5640_power(sensor, true);
++ usleep_range(5000, 10000);
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++ usleep_range(1000, 2000);
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++ usleep_range(20000, 25000);
++}
++
++static int ov5640_set_power_on(struct ov5640_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret;
++
++ ret = clk_prepare_enable(sensor->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ return ret;
++ }
++
++ ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
++ sensor->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ goto xclk_off;
++ }
++
++ ov5640_reset(sensor);
++ ov5640_power(sensor, true);
++
++ ret = ov5640_init_slave_id(sensor);
++ if (ret)
++ goto power_off;
++
++ return 0;
++
++power_off:
++ ov5640_power(sensor, false);
++ regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
++xclk_off:
++ clk_disable_unprepare(sensor->xclk);
++ return ret;
++}
++
++static void ov5640_set_power_off(struct ov5640_dev *sensor)
++{
++ ov5640_power(sensor, false);
++ regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
++ clk_disable_unprepare(sensor->xclk);
++}
++
++static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on)
++{
++ int ret;
++
++ if (!on) {
++ /* Reset MIPI bus settings to their default values. */
++ ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++ ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x04);
++ ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x00);
++ return 0;
++ }
++
++ /*
++ * Power up MIPI HS Tx and LS Rx; 2 data lanes mode
++ *
++ * 0x300e = 0x40
++ * [7:5] = 010 : 2 data lanes mode (see FIXME note in
++ * "ov5640_set_stream_mipi()")
++ * [4] = 0 : Power up MIPI HS Tx
++ * [3] = 0 : Power up MIPI LS Rx
++ * [2] = 0 : MIPI interface disabled
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x40);
++ if (ret)
++ return ret;
++
++ /*
++ * Gate clock and set LP11 in 'no packets mode' (idle)
++ *
++ * 0x4800 = 0x24
++ * [5] = 1 : Gate clock when 'no packets'
++ * [2] = 1 : MIPI bus in LP11 when 'no packets'
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x24);
++ if (ret)
++ return ret;
++
++ /*
++ * Set data lanes and clock in LP11 when 'sleeping'
++ *
++ * 0x3019 = 0x70
++ * [6] = 1 : MIPI data lane 2 in LP11 when 'sleeping'
++ * [5] = 1 : MIPI data lane 1 in LP11 when 'sleeping'
++ * [4] = 1 : MIPI clock lane in LP11 when 'sleeping'
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x70);
++ if (ret)
++ return ret;
++
++ /* Give lanes some time to coax into LP11 state. */
++ usleep_range(500, 1000);
++
++ return 0;
++}
++
++static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on)
++{
++ unsigned int flags = sensor->ep.bus.parallel.flags;
++ bool bt656 = sensor->ep.bus_type == V4L2_MBUS_BT656;
++ u8 polarities = 0;
++ int ret;
++
++ if (!on) {
++ /* Reset settings to their default values. */
++ ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, 0x00);
++ ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++ ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, 0x20);
++ ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, 0x00);
++ ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0x00);
++ return 0;
++ }
++
++ /*
++ * Note about parallel port configuration.
++ *
++ * When configured in parallel mode, the OV5640 will
++ * output 10 bits data on DVP data lines [9:0].
++ * If only 8 bits data are wanted, the 8 bits data lines
++ * of the camera interface must be physically connected
++ * on the DVP data lines [9:2].
++ *
++ * Control lines polarity can be configured through
++ * devicetree endpoint control lines properties.
++ * If no endpoint control lines properties are set,
++ * polarity will be as below:
++ * - VSYNC: active high
++ * - HREF: active low
++ * - PCLK: active low
++ *
++ * VSYNC & HREF are not configured if BT656 bus mode is selected
++ */
++
++ /*
++ * BT656 embedded synchronization configuration
++ *
++ * CCIR656 CTRL00
++ * - [7]: SYNC code selection (0: auto generate sync code,
++ * 1: sync code from regs 0x4732-0x4735)
++ * - [6]: f value in CCIR656 SYNC code when fixed f value
++ * - [5]: Fixed f value
++ * - [4:3]: Blank toggle data options (00: data=1'h040/1'h200,
++ * 01: data from regs 0x4736-0x4738, 10: always keep 0)
++ * - [1]: Clip data disable
++ * - [0]: CCIR656 mode enable
++ *
++ * Default CCIR656 SAV/EAV mode with default codes
++ * SAV=0xff000080 & EAV=0xff00009d is enabled here with settings:
++ * - CCIR656 mode enable
++ * - auto generation of sync codes
++ * - blank toggle data 1'h040/1'h200
++ * - clip reserved data (0x00 & 0xff changed to 0x01 & 0xfe)
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00,
++ bt656 ? 0x01 : 0x00);
++ if (ret)
++ return ret;
++
++ /*
++ * configure parallel port control lines polarity
++ *
++ * POLARITY CTRL0
++ * - [5]: PCLK polarity (0: active low, 1: active high)
++ * - [1]: HREF polarity (0: active low, 1: active high)
++ * - [0]: VSYNC polarity (mismatch here between
++ * datasheet and hardware, 0 is active high
++ * and 1 is active low...)
++ */
++ if (!bt656) {
++ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++ polarities |= BIT(1);
++ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
++ polarities |= BIT(0);
++ }
++ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
++ polarities |= BIT(5);
++
++ ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, polarities);
++ if (ret)
++ return ret;
++
++ /*
++ * powerdown MIPI TX/RX PHY & enable DVP
++ *
++ * MIPI CONTROL 00
++ * [4] = 1 : Power down MIPI HS Tx
++ * [3] = 1 : Power down MIPI LS Rx
++ * [2] = 0 : DVP enable (MIPI disable)
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++ if (ret)
++ return ret;
++
++ /*
++ * enable VSYNC/HREF/PCLK DVP control lines
++ * & D[9:6] DVP data lines
++ *
++ * PAD OUTPUT ENABLE 01
++ * - 6: VSYNC output enable
++ * - 5: HREF output enable
++ * - 4: PCLK output enable
++ * - [3:0]: D[9:6] output enable
++ */
++ ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01,
++ bt656 ? 0x1f : 0x7f);
++ if (ret)
++ return ret;
++
++ /*
++ * enable D[5:0] DVP data lines
++ *
++ * PAD OUTPUT ENABLE 02
++ * - [7:2]: D[5:0] output enable
++ */
++ return ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0xfc);
++}
++
++static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
++{
++ int ret = 0;
++
++ if (on) {
++ ret = ov5640_set_power_on(sensor);
++ if (ret)
++ return ret;
++
++ ret = ov5640_restore_mode(sensor);
++ if (ret)
++ goto power_off;
++ }
++
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++ ret = ov5640_set_power_mipi(sensor, on);
++ else
++ ret = ov5640_set_power_dvp(sensor, on);
++ if (ret)
++ goto power_off;
++
++ if (!on)
++ ov5640_set_power_off(sensor);
++
++ return 0;
++
++power_off:
++ ov5640_set_power_off(sensor);
++ return ret;
++}
++
++/* --------------- Subdev Operations --------------- */
++
++static int ov5640_s_power(struct v4l2_subdev *sd, int on)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ int ret = 0;
++
++ mutex_lock(&sensor->lock);
++
++ /*
++ * If the power count is modified from 0 to != 0 or from != 0 to 0,
++ * update the power state.
++ */
++ if (sensor->power_count == !on) {
++ ret = ov5640_set_power(sensor, !!on);
++ if (ret)
++ goto out;
++ }
++
++ /* Update the power count. */
++ sensor->power_count += on ? 1 : -1;
++ WARN_ON(sensor->power_count < 0);
++out:
++ mutex_unlock(&sensor->lock);
++
++ if (on && !ret && sensor->power_count == 1) {
++ /* restore controls */
++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++ }
++
++ return ret;
++}
++
++static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
++ struct v4l2_fract *fi,
++ u32 width, u32 height)
++{
++ const struct ov5640_mode_info *mode;
++ enum ov5640_frame_rate rate = OV5640_15_FPS;
++ int minfps, maxfps, best_fps, fps;
++ int i;
++
++ minfps = ov5640_framerates[OV5640_15_FPS];
++ maxfps = ov5640_framerates[OV5640_60_FPS];
++
++ if (fi->numerator == 0) {
++ fi->denominator = maxfps;
++ fi->numerator = 1;
++ rate = OV5640_60_FPS;
++ goto find_mode;
++ }
++
++ fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++ minfps, maxfps);
++
++ best_fps = minfps;
++ for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
++ int curr_fps = ov5640_framerates[i];
++
++ if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++ best_fps = curr_fps;
++ rate = i;
++ }
++ }
++
++ fi->numerator = 1;
++ fi->denominator = best_fps;
++
++find_mode:
++ mode = ov5640_find_mode(sensor, rate, width, height, false);
++ return mode ? rate : -EINVAL;
++}
++
++static int ov5640_get_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ struct v4l2_mbus_framefmt *fmt;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++ format->pad);
++ else
++ fmt = &sensor->fmt;
++
++ format->format = *fmt;
++
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
++ struct v4l2_mbus_framefmt *fmt,
++ enum ov5640_frame_rate fr,
++ const struct ov5640_mode_info **new_mode)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ const struct ov5640_mode_info *mode;
++ int i;
++
++ mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
++ if (!mode)
++ return -EINVAL;
++ fmt->width = mode->hact;
++ fmt->height = mode->vact;
++
++ if (new_mode)
++ *new_mode = mode;
++
++ for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
++ if (ov5640_formats[i].code == fmt->code)
++ break;
++ if (i >= ARRAY_SIZE(ov5640_formats))
++ i = 0;
++
++ fmt->code = ov5640_formats[i].code;
++ fmt->colorspace = ov5640_formats[i].colorspace;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++ return 0;
++}
++
++static int ov5640_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ const struct ov5640_mode_info *new_mode;
++ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++ struct v4l2_mbus_framefmt *fmt;
++ int ret;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ ret = ov5640_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++ if (ret)
++ goto out;
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(sd, state, 0);
++ else
++ fmt = &sensor->fmt;
++
++ if (mbus_fmt->code != sensor->fmt.code)
++ sensor->pending_fmt_change = true;
++
++ *fmt = *mbus_fmt;
++
++ if (new_mode != sensor->current_mode) {
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++ if (new_mode->max_fps < sensor->current_fr) {
++ sensor->current_fr = new_mode->max_fps;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator =
++ ov5640_framerates[sensor->current_fr];
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov5640_calc_pixel_rate(sensor));
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int ov5640_set_framefmt(struct ov5640_dev *sensor,
++ struct v4l2_mbus_framefmt *format)
++{
++ int ret = 0;
++ bool is_jpeg = false;
++ u8 fmt, mux;
++
++ switch (format->code) {
++ case MEDIA_BUS_FMT_UYVY8_2X8:
++ /* YUV422, UYVY */
++ fmt = 0x3f;
++ mux = OV5640_FMT_MUX_YUV422;
++ break;
++ case MEDIA_BUS_FMT_YUYV8_2X8:
++ /* YUV422, YUYV */
++ fmt = 0x30;
++ mux = OV5640_FMT_MUX_YUV422;
++ break;
++ case MEDIA_BUS_FMT_RGB565_2X8_LE:
++ /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
++ fmt = 0x6F;
++ mux = OV5640_FMT_MUX_RGB;
++ break;
++ case MEDIA_BUS_FMT_RGB565_2X8_BE:
++ /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
++ fmt = 0x61;
++ mux = OV5640_FMT_MUX_RGB;
++ break;
++ case MEDIA_BUS_FMT_JPEG_1X8:
++ /* YUV422, YUYV */
++ fmt = 0x30;
++ mux = OV5640_FMT_MUX_YUV422;
++ is_jpeg = true;
++ break;
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ /* Raw, BGBG... / GRGR... */
++ fmt = 0x00;
++ mux = OV5640_FMT_MUX_RAW_DPC;
++ break;
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ /* Raw bayer, GBGB... / RGRG... */
++ fmt = 0x01;
++ mux = OV5640_FMT_MUX_RAW_DPC;
++ break;
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ /* Raw bayer, GRGR... / BGBG... */
++ fmt = 0x02;
++ mux = OV5640_FMT_MUX_RAW_DPC;
++ break;
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ /* Raw bayer, RGRG... / GBGB... */
++ fmt = 0x03;
++ mux = OV5640_FMT_MUX_RAW_DPC;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* FORMAT CONTROL00: YUV and RGB formatting */
++ ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt);
++ if (ret)
++ return ret;
++
++ /* FORMAT MUX CONTROL: ISP YUV or RGB */
++ ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux);
++ if (ret)
++ return ret;
++
++ /*
++ * TIMING TC REG21:
++ * - [5]: JPEG enable
++ */
++ ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++ BIT(5), is_jpeg ? BIT(5) : 0);
++ if (ret)
++ return ret;
++
++ /*
++ * SYSTEM RESET02:
++ * - [4]: Reset JFIFO
++ * - [3]: Reset SFIFO
++ * - [2]: Reset JPEG
++ */
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
++ BIT(4) | BIT(3) | BIT(2),
++ is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
++ if (ret)
++ return ret;
++
++ /*
++ * CLOCK ENABLE02:
++ * - [5]: Enable JPEG 2x clock
++ * - [3]: Enable JPEG clock
++ */
++ return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
++ BIT(5) | BIT(3),
++ is_jpeg ? (BIT(5) | BIT(3)) : 0);
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
++{
++ int ret;
++
++ if (value) {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++ BIT(0), BIT(0));
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
++ } else {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
++ }
++
++ return ret;
++}
++
++static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
++{
++ int ret;
++
++ if (value) {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++ BIT(2), BIT(2));
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
++ value & 0xff);
++ } else {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
++ }
++
++ return ret;
++}
++
++static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
++{
++ int ret;
++
++ if (value) {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++ BIT(1), BIT(1));
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
++ value & 0xff);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
++ value & 0xff);
++ } else {
++ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
++ }
++
++ return ret;
++}
++
++static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
++{
++ int ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
++ BIT(0), awb ? 0 : 1);
++ if (ret)
++ return ret;
++
++ if (!awb) {
++ u16 red = (u16)sensor->ctrls.red_balance->val;
++ u16 blue = (u16)sensor->ctrls.blue_balance->val;
++
++ ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
++ if (ret)
++ return ret;
++ ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
++ }
++
++ return ret;
++}
++
++static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor,
++ enum v4l2_exposure_auto_type auto_exposure)
++{
++ struct ov5640_ctrls *ctrls = &sensor->ctrls;
++ bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++ int ret = 0;
++
++ if (ctrls->auto_exp->is_new) {
++ ret = ov5640_set_autoexposure(sensor, auto_exp);
++ if (ret)
++ return ret;
++ }
++
++ if (!auto_exp && ctrls->exposure->is_new) {
++ u16 max_exp;
++
++ ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
++ &max_exp);
++ if (ret)
++ return ret;
++ ret = ov5640_get_vts(sensor);
++ if (ret < 0)
++ return ret;
++ max_exp += ret;
++ ret = 0;
++
++ if (ctrls->exposure->val < max_exp)
++ ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
++ }
++
++ return ret;
++}
++
++static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
++{
++ struct ov5640_ctrls *ctrls = &sensor->ctrls;
++ int ret = 0;
++
++ if (ctrls->auto_gain->is_new) {
++ ret = ov5640_set_autogain(sensor, auto_gain);
++ if (ret)
++ return ret;
++ }
++
++ if (!auto_gain && ctrls->gain->is_new)
++ ret = ov5640_set_gain(sensor, ctrls->gain->val);
++
++ return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++ "Disabled",
++ "Color bars",
++ "Color bars w/ rolling bar",
++ "Color squares",
++ "Color squares w/ rolling bar",
++};
++
++#define OV5640_TEST_ENABLE BIT(7)
++#define OV5640_TEST_ROLLING BIT(6) /* rolling horizontal bar */
++#define OV5640_TEST_TRANSPARENT BIT(5)
++#define OV5640_TEST_SQUARE_BW BIT(4) /* black & white squares */
++#define OV5640_TEST_BAR_STANDARD (0 << 2)
++#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2)
++#define OV5640_TEST_BAR_HOR_CHANGE (2 << 2)
++#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2)
++#define OV5640_TEST_BAR (0 << 0)
++#define OV5640_TEST_RANDOM (1 << 0)
++#define OV5640_TEST_SQUARE (2 << 0)
++#define OV5640_TEST_BLACK (3 << 0)
++
++static const u8 test_pattern_val[] = {
++ 0,
++ OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 |
++ OV5640_TEST_BAR,
++ OV5640_TEST_ENABLE | OV5640_TEST_ROLLING |
++ OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR,
++ OV5640_TEST_ENABLE | OV5640_TEST_SQUARE,
++ OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE,
++};
++
++static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
++{
++ return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
++ test_pattern_val[value]);
++}
++
++static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
++{
++ int ret;
++
++ ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
++ (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
++ 0 : BIT(7));
++ if (ret)
++ return ret;
++
++ return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
++ (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
++ BIT(2) : 0);
++}
++
++static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
++{
++ /*
++ * If sensor is mounted upside down, mirror logic is inversed.
++ *
++ * Sensor is a BSI (Back Side Illuminated) one,
++ * so image captured is physically mirrored.
++ * This is why mirror logic is inversed in
++ * order to cancel this mirror effect.
++ */
++
++ /*
++ * TIMING TC REG21:
++ * - [2]: ISP mirror
++ * - [1]: Sensor mirror
++ */
++ return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++ BIT(2) | BIT(1),
++ (!(value ^ sensor->upside_down)) ?
++ (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
++{
++ /* If sensor is mounted upside down, flip logic is inversed */
++
++ /*
++ * TIMING TC REG20:
++ * - [2]: ISP vflip
++ * - [1]: Sensor vflip
++ */
++ return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
++ BIT(2) | BIT(1),
++ (value ^ sensor->upside_down) ?
++ (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ int val;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ val = ov5640_get_gain(sensor);
++ if (val < 0)
++ return val;
++ sensor->ctrls.gain->val = val;
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ val = ov5640_get_exposure(sensor);
++ if (val < 0)
++ return val;
++ sensor->ctrls.exposure->val = val;
++ break;
++ }
++
++ return 0;
++}
++
++static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ int ret;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored right after power-up.
++ */
++ if (sensor->power_count == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
++ break;
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HUE:
++ ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
++ break;
++ case V4L2_CID_CONTRAST:
++ ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
++ break;
++ case V4L2_CID_SATURATION:
++ ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
++ break;
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = ov5640_set_ctrl_hflip(sensor, ctrl->val);
++ break;
++ case V4L2_CID_VFLIP:
++ ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
++ .g_volatile_ctrl = ov5640_g_volatile_ctrl,
++ .s_ctrl = ov5640_s_ctrl,
++};
++
++static int ov5640_init_controls(struct ov5640_dev *sensor)
++{
++ const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
++ struct ov5640_ctrls *ctrls = &sensor->ctrls;
++ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++ int ret;
++
++ v4l2_ctrl_handler_init(hdl, 32);
++
++ /* we can use our own mutex for the ctrl lock */
++ hdl->lock = &sensor->lock;
++
++ /* Clock related controls */
++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++ 0, INT_MAX, 1,
++ ov5640_calc_pixel_rate(sensor));
++
++ /* Auto/manual white balance */
++ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, 1, 1, 1);
++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++ 0, 4095, 1, 0);
++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++ 0, 4095, 1, 0);
++ /* Auto/manual exposure */
++ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_EXPOSURE_AUTO,
++ V4L2_EXPOSURE_MANUAL, 0,
++ V4L2_EXPOSURE_AUTO);
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 0, 65535, 1, 0);
++ /* Auto/manual gain */
++ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++ 0, 1, 1, 1);
++ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++ 0, 1023, 1, 0);
++
++ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++ 0, 255, 1, 64);
++ ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++ 0, 359, 1, 0);
++ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++ 0, 255, 1, 0);
++ ctrls->test_pattern =
++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(test_pattern_menu) - 1,
++ 0, 0, test_pattern_menu);
++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++ 0, 1, 1, 0);
++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++ 0, 1, 1, 0);
++
++ ctrls->light_freq =
++ v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_POWER_LINE_FREQUENCY,
++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++ if (hdl->error) {
++ ret = hdl->error;
++ goto free_ctrls;
++ }
++
++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++ sensor->sd.ctrl_handler = hdl;
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(hdl);
++ return ret;
++}
++
++static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ if (fse->pad != 0)
++ return -EINVAL;
++ if (fse->index >= OV5640_NUM_MODES)
++ return -EINVAL;
++
++ fse->min_width =
++ ov5640_mode_data[fse->index].hact;
++ fse->max_width = fse->min_width;
++ fse->min_height =
++ ov5640_mode_data[fse->index].vact;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int ov5640_enum_frame_interval(
++ struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_interval_enum *fie)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ struct v4l2_fract tpf;
++ int ret;
++
++ if (fie->pad != 0)
++ return -EINVAL;
++ if (fie->index >= OV5640_NUM_FRAMERATES)
++ return -EINVAL;
++
++ tpf.numerator = 1;
++ tpf.denominator = ov5640_framerates[fie->index];
++
++ ret = ov5640_try_frame_interval(sensor, &tpf,
++ fie->width, fie->height);
++ if (ret < 0)
++ return -EINVAL;
++
++ fie->interval = tpf;
++ return 0;
++}
++
++static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++
++ mutex_lock(&sensor->lock);
++ fi->interval = sensor->frame_interval;
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ const struct ov5640_mode_info *mode;
++ int frame_rate, ret = 0;
++
++ if (fi->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ mode = sensor->current_mode;
++
++ frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
++ mode->hact, mode->vact);
++ if (frame_rate < 0) {
++ /* Always return a valid frame interval value */
++ fi->interval = sensor->frame_interval;
++ goto out;
++ }
++
++ mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
++ mode->vact, true);
++ if (!mode) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (mode != sensor->current_mode ||
++ frame_rate != sensor->current_fr) {
++ sensor->current_fr = frame_rate;
++ sensor->frame_interval = fi->interval;
++ sensor->current_mode = mode;
++ sensor->pending_mode_change = true;
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ ov5640_calc_pixel_rate(sensor));
++ }
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ if (code->pad != 0)
++ return -EINVAL;
++ if (code->index >= ARRAY_SIZE(ov5640_formats))
++ return -EINVAL;
++
++ code->code = ov5640_formats[code->index].code;
++ return 0;
++}
++
++static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++ int ret = 0;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming == !enable) {
++ if (enable && sensor->pending_mode_change) {
++ ret = ov5640_set_mode(sensor);
++ if (ret)
++ goto out;
++ }
++
++ if (enable && sensor->pending_fmt_change) {
++ ret = ov5640_set_framefmt(sensor, &sensor->fmt);
++ if (ret)
++ goto out;
++ sensor->pending_fmt_change = false;
++ }
++
++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++ ret = ov5640_set_stream_mipi(sensor, enable);
++ else
++ ret = ov5640_set_stream_dvp(sensor, enable);
++
++ if (ret)
++ goto out;
++ }
++ sensor->streaming += enable ? 1 : -1;
++ WARN_ON(sensor->streaming < 0);
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++int ov5640_skip_frames(struct v4l2_subdev *sd, u32 *frames)
++{
++ *frames = OV5640_SKIP_FRAMES;
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops ov5640_core_ops = {
++ .s_power = ov5640_s_power,
++ .log_status = v4l2_ctrl_subdev_log_status,
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov5640_video_ops = {
++ .g_frame_interval = ov5640_g_frame_interval,
++ .s_frame_interval = ov5640_s_frame_interval,
++ .s_stream = ov5640_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
++ .enum_mbus_code = ov5640_enum_mbus_code,
++ .get_fmt = ov5640_get_fmt,
++ .set_fmt = ov5640_set_fmt,
++ .enum_frame_size = ov5640_enum_frame_size,
++ .enum_frame_interval = ov5640_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_sensor_ops ov5640_sensor_ops = {
++ .g_skip_frames = ov5640_skip_frames,
++};
++
++static const struct v4l2_subdev_ops ov5640_subdev_ops = {
++ .core = &ov5640_core_ops,
++ .video = &ov5640_video_ops,
++ .pad = &ov5640_pad_ops,
++ .sensor = &ov5640_sensor_ops,
++};
++
++static int ov5640_get_regulators(struct ov5640_dev *sensor)
++{
++ int i;
++
++ for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
++ sensor->supplies[i].supply = ov5640_supply_name[i];
++
++ return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++ OV5640_NUM_SUPPLIES,
++ sensor->supplies);
++}
++
++static int ov5640_check_chip_id(struct ov5640_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret = 0;
++ u16 chip_id;
++
++ ret = ov5640_set_power_on(sensor);
++ if (ret)
++ return ret;
++
++ ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to read chip identifier\n",
++ __func__);
++ goto power_off;
++ }
++
++ if (chip_id != OV5640_CHIP_ID) {
++ dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++ __func__, OV5640_CHIP_ID, chip_id);
++ ret = -ENXIO;
++ }
++ dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, chip_id);
++
++power_off:
++ ov5640_set_power_off(sensor);
++ return ret;
++}
++
++static int ov5640_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct fwnode_handle *endpoint;
++ struct ov5640_dev *sensor;
++ struct v4l2_mbus_framefmt *fmt;
++ u32 rotation;
++ int ret;
++
++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++ if (!sensor)
++ return -ENOMEM;
++
++ sensor->i2c_client = client;
++
++ /*
++ * default init sequence initialize sensor to
++ * YUV422 UYVY VGA@30fps
++ */
++ fmt = &sensor->fmt;
++ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = 640;
++ fmt->height = 480;
++ fmt->field = V4L2_FIELD_NONE;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
++ sensor->current_fr = OV5640_30_FPS;
++ sensor->current_mode =
++ &ov5640_mode_data[OV5640_MODE_VGA_640_480];
++ sensor->last_mode = sensor->current_mode;
++
++ sensor->ae_target = 52;
++
++ /* optional indication of physical rotation of sensor */
++ ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++ &rotation);
++ if (!ret) {
++ switch (rotation) {
++ case 180:
++ sensor->upside_down = true;
++ fallthrough;
++ case 0:
++ break;
++ default:
++ dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++ rotation);
++ }
++ }
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++ NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++ fwnode_handle_put(endpoint);
++ if (ret) {
++ dev_err(dev, "Could not parse endpoint\n");
++ return ret;
++ }
++
++ if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
++ sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
++ sensor->ep.bus_type != V4L2_MBUS_BT656) {
++ dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++ return -EINVAL;
++ }
++
++ /* get system clock (xclk) */
++ sensor->xclk = devm_clk_get(dev, "xclk");
++ if (IS_ERR(sensor->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(sensor->xclk);
++ }
++
++ sensor->xclk_freq = clk_get_rate(sensor->xclk);
++ if (sensor->xclk_freq < OV5640_XCLK_MIN ||
++ sensor->xclk_freq > OV5640_XCLK_MAX) {
++ dev_err(dev, "xclk frequency out of range: %d Hz\n",
++ sensor->xclk_freq);
++ return -EINVAL;
++ }
++
++ /* request optional power down pin */
++ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->pwdn_gpio))
++ return PTR_ERR(sensor->pwdn_gpio);
++
++ /* request optional reset pin */
++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->reset_gpio))
++ return PTR_ERR(sensor->reset_gpio);
++
++ v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
++
++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++ if (ret)
++ return ret;
++
++ ret = ov5640_get_regulators(sensor);
++ if (ret)
++ return ret;
++
++ mutex_init(&sensor->lock);
++
++ ret = ov5640_check_chip_id(sensor);
++ if (ret)
++ goto entity_cleanup;
++
++ ret = ov5640_init_controls(sensor);
++ if (ret)
++ goto entity_cleanup;
++
++ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++ if (ret)
++ goto free_ctrls;
++
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_cleanup:
++ media_entity_cleanup(&sensor->sd.entity);
++ mutex_destroy(&sensor->lock);
++ return ret;
++}
++
++static int ov5640_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov5640_dev *sensor = to_ov5640_dev(sd);
++
++ v4l2_async_unregister_subdev(&sensor->sd);
++ media_entity_cleanup(&sensor->sd.entity);
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++ mutex_destroy(&sensor->lock);
++
++ return 0;
++}
++
++static const struct i2c_device_id ov5640_id[] = {
++ {"ov5640", 0},
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, ov5640_id);
++
++static const struct of_device_id ov5640_dt_ids[] = {
++ { .compatible = "ovti,ov5640" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
++
++static struct i2c_driver ov5640_i2c_driver = {
++ .driver = {
++ .name = "ov5640",
++ .of_match_table = ov5640_dt_ids,
++ },
++ .id_table = ov5640_id,
++ .probe_new = ov5640_probe,
++ .remove = ov5640_remove,
++};
++
++module_i2c_driver(ov5640_i2c_driver);
++
++MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/sc2235.c
+@@ -0,0 +1,1914 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++/* min/typical/max system clock (xclk) frequencies */
++#define SC2235_XCLK_MIN 6000000
++#define SC2235_XCLK_MAX 27000000
++
++#define SC2235_CHIP_ID (0x2235)
++
++#define SC2235_REG_CHIP_ID 0x3107
++#define SC2235_REG_AEC_PK_MANUAL 0x3e03
++#define SC2235_REG_AEC_PK_EXPOSURE_HI 0x3e01
++#define SC2235_REG_AEC_PK_EXPOSURE_LO 0x3e02
++#define SC2235_REG_AEC_PK_REAL_GAIN 0x3e08
++#define SC2235_REG_TIMING_HTS 0x320c
++#define SC2235_REG_TIMING_VTS 0x320e
++#define SC2235_REG_TEST_SET0 0x4501
++#define SC2235_REG_TEST_SET1 0x3902
++#define SC2235_REG_TIMING_TC_REG21 0x3221
++#define SC2235_REG_SC_PLL_CTRL0 0x3039
++#define SC2235_REG_SC_PLL_CTRL1 0x303a
++#define SC2235_REG_STREAM_ON 0x0100
++
++enum sc2235_mode_id {
++ SC2235_MODE_1080P_1920_1080 = 0,
++ SC2235_NUM_MODES,
++};
++
++enum sc2235_frame_rate {
++ SC2235_15_FPS = 0,
++ SC2235_30_FPS,
++ SC2235_NUM_FRAMERATES,
++};
++
++struct sc2235_pixfmt {
++ u32 code;
++ u32 colorspace;
++};
++
++static const struct sc2235_pixfmt sc2235_formats[] = {
++ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, },
++};
++
++static const int sc2235_framerates[] = {
++ [SC2235_15_FPS] = 15,
++ [SC2235_30_FPS] = 30,
++};
++
++/* regulator supplies */
++static const char * const sc2235_supply_name[] = {
++ "DOVDD", /* Digital I/O (1.8V) supply */
++ "AVDD", /* Analog (2.8V) supply */
++ "DVDD", /* Digital Core (1.5V) supply */
++};
++
++#define SC2235_NUM_SUPPLIES ARRAY_SIZE(sc2235_supply_name)
++
++struct reg_value {
++ u16 reg_addr;
++ u8 val;
++ u8 mask;
++ u32 delay_ms;
++};
++
++struct sc2235_mode_info {
++ enum sc2235_mode_id id;
++ u32 hact;
++ u32 htot;
++ u32 vact;
++ u32 vtot;
++ const struct reg_value *reg_data;
++ u32 reg_data_size;
++ u32 max_fps;
++};
++
++struct sc2235_ctrls {
++ struct v4l2_ctrl_handler handler;
++ struct v4l2_ctrl *pixel_rate;
++ struct {
++ struct v4l2_ctrl *auto_exp;
++ struct v4l2_ctrl *exposure;
++ };
++ struct {
++ struct v4l2_ctrl *auto_wb;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *red_balance;
++ };
++ struct {
++ struct v4l2_ctrl *auto_gain;
++ struct v4l2_ctrl *gain;
++ };
++ struct v4l2_ctrl *brightness;
++ struct v4l2_ctrl *light_freq;
++ struct v4l2_ctrl *saturation;
++ struct v4l2_ctrl *contrast;
++ struct v4l2_ctrl *hue;
++ struct v4l2_ctrl *test_pattern;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vflip;
++};
++
++struct sc2235_dev {
++ struct i2c_client *i2c_client;
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++ struct clk *xclk; /* system clock to SC2235 */
++ u32 xclk_freq;
++
++ struct regulator_bulk_data supplies[SC2235_NUM_SUPPLIES];
++ struct gpio_desc *reset_gpio;
++ struct gpio_desc *pwdn_gpio;
++ bool upside_down;
++
++ /* lock to protect all members below */
++ struct mutex lock;
++
++ struct v4l2_mbus_framefmt fmt;
++ bool pending_fmt_change;
++
++ const struct sc2235_mode_info *current_mode;
++ const struct sc2235_mode_info *last_mode;
++ enum sc2235_frame_rate current_fr;
++ struct v4l2_fract frame_interval;
++
++ struct sc2235_ctrls ctrls;
++
++ bool pending_mode_change;
++ int streaming;
++};
++
++static inline struct sc2235_dev *to_sc2235_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct sc2235_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct sc2235_dev,
++ ctrls.handler)->sd;
++}
++
++/* sc2235 initial register 30fps*/
++static struct reg_value sc2235_init_regs_tbl_1080[] = {
++ {0x0103, 0x01, 0, 50},
++ {0x0100, 0x00, 0, 0},
++ {0x3039, 0x80, 0, 0},
++ {0x3621, 0x28, 0, 0},
++
++ {0x3309, 0x60, 0, 0},
++ {0x331f, 0x4d, 0, 0},
++ {0x3321, 0x4f, 0, 0},
++ {0x33b5, 0x10, 0, 0},
++
++ {0x3303, 0x20, 0, 0},
++ {0x331e, 0x0d, 0, 0},
++ {0x3320, 0x0f, 0, 0},
++
++ {0x3622, 0x02, 0, 0},
++ {0x3633, 0x42, 0, 0},
++ {0x3634, 0x42, 0, 0},
++
++ {0x3306, 0x66, 0, 0},
++ {0x330b, 0xd1, 0, 0},
++
++ {0x3301, 0x0e, 0, 0},
++
++ {0x320c, 0x08, 0, 0},
++ {0x320d, 0x98, 0, 0},
++
++ {0x3364, 0x05, 0, 0}, // [2] 1: write at sampling ending
++
++ {0x363c, 0x28, 0, 0}, //bypass nvdd
++ {0x363b, 0x0a, 0, 0}, //HVDD
++ {0x3635, 0xa0, 0, 0}, //TXVDD
++
++ {0x4500, 0x59, 0, 0},
++ {0x3d08, 0x00, 0, 0},
++ {0x3908, 0x11, 0, 0},
++
++ {0x363c, 0x08, 0, 0},
++
++ {0x3e03, 0x03, 0, 0},
++ {0x3e01, 0x46, 0, 0},
++
++ //0703
++ {0x3381, 0x0a, 0, 0},
++ {0x3348, 0x09, 0, 0},
++ {0x3349, 0x50, 0, 0},
++ {0x334a, 0x02, 0, 0},
++ {0x334b, 0x60, 0, 0},
++
++ {0x3380, 0x04, 0, 0},
++ {0x3340, 0x06, 0, 0},
++ {0x3341, 0x50, 0, 0},
++ {0x3342, 0x02, 0, 0},
++ {0x3343, 0x60, 0, 0},
++
++ //0707
++
++ {0x3632, 0x88, 0, 0}, //anti sm
++ {0x3309, 0xa0, 0, 0},
++ {0x331f, 0x8d, 0, 0},
++ {0x3321, 0x8f, 0, 0},
++
++ {0x335e, 0x01, 0, 0}, //ana dithering
++ {0x335f, 0x03, 0, 0},
++ {0x337c, 0x04, 0, 0},
++ {0x337d, 0x06, 0, 0},
++ {0x33a0, 0x05, 0, 0},
++ {0x3301, 0x05, 0, 0},
++
++ {0x337f, 0x03, 0, 0},
++ {0x3368, 0x02, 0, 0},
++ {0x3369, 0x00, 0, 0},
++ {0x336a, 0x00, 0, 0},
++ {0x336b, 0x00, 0, 0},
++ {0x3367, 0x08, 0, 0},
++ {0x330e, 0x30, 0, 0},
++
++ {0x3366, 0x7c, 0, 0}, // div_rst gap
++
++ {0x3635, 0xc1, 0, 0},
++ {0x363b, 0x09, 0, 0},
++ {0x363c, 0x07, 0, 0},
++
++ {0x391e, 0x00, 0, 0},
++
++ {0x3637, 0x14, 0, 0}, //fullwell 7K
++
++ {0x3306, 0x54, 0, 0},
++ {0x330b, 0xd8, 0, 0},
++ {0x366e, 0x08, 0, 0}, // ofs auto en [3]
++ {0x366f, 0x2f, 0, 0},
++
++ {0x3631, 0x84, 0, 0},
++ {0x3630, 0x48, 0, 0},
++ {0x3622, 0x06, 0, 0},
++
++ //ramp by sc
++ {0x3638, 0x1f, 0, 0},
++ {0x3625, 0x02, 0, 0},
++ {0x3636, 0x24, 0, 0},
++
++ //0714
++ {0x3348, 0x08, 0, 0},
++ {0x3e03, 0x0b, 0, 0},
++
++ //7.17 fpn
++ {0x3342, 0x03, 0, 0},
++ {0x3343, 0xa0, 0, 0},
++ {0x334a, 0x03, 0, 0},
++ {0x334b, 0xa0, 0, 0},
++
++ //0718
++ {0x3343, 0xb0, 0, 0},
++ {0x334b, 0xb0, 0, 0},
++
++ //0720
++ //digital ctrl
++ {0x3802, 0x01, 0, 0},
++ {0x3235, 0x04, 0, 0},
++ {0x3236, 0x63, 0, 0}, // vts-2
++
++ //fpn
++ {0x3343, 0xd0, 0, 0},
++ {0x334b, 0xd0, 0, 0},
++ {0x3348, 0x07, 0, 0},
++ {0x3349, 0x80, 0, 0},
++
++ //0724
++ {0x391b, 0x4d, 0, 0},
++
++ {0x3342, 0x04, 0, 0},
++ {0x3343, 0x20, 0, 0},
++ {0x334a, 0x04, 0, 0},
++ {0x334b, 0x20, 0, 0},
++
++ //0804
++ {0x3222, 0x29, 0, 0},
++ {0x3901, 0x02, 0, 0},
++
++ //0808
++
++ // auto blc
++ {0x3900, 0xD5, 0, 0}, // Bit[0]: blc_enable
++ {0x3902, 0x45, 0, 0}, // Bit[6]: blc_auto_en
++
++ // blc target
++ {0x3907, 0x00, 0, 0},
++ {0x3908, 0x00, 0, 0},
++
++ // auto dpc
++ {0x5000, 0x00, 0, 0}, // Bit[2]: white dead pixel cancel enable, Bit[1]: black dead pixel cancel enable
++
++ //digital ctrl
++ {0x3f00, 0x07, 0, 0}, // bit[2] = 1
++ {0x3f04, 0x08, 0, 0},
++ {0x3f05, 0x74, 0, 0}, // hts - { 0x24
++
++ //0809
++ {0x330b, 0xc8, 0, 0},
++
++ //0817
++ {0x3306, 0x4a, 0, 0},
++ {0x330b, 0xca, 0, 0},
++ {0x3639, 0x09, 0, 0},
++
++ //manual DPC
++ {0x5780, 0xff, 0, 0},
++ {0x5781, 0x04, 0, 0},
++ {0x5785, 0x18, 0, 0},
++
++ //0822
++ {0x3039, 0x35, 0, 0}, //fps
++ {0x303a, 0x2e, 0, 0},
++ {0x3034, 0x05, 0, 0},
++ {0x3035, 0x2a, 0, 0},
++
++ {0x320c, 0x08, 0, 0},
++ {0x320d, 0xca, 0, 0},
++ {0x320e, 0x04, 0, 0},
++ {0x320f, 0xb0, 0, 0},
++
++ {0x3f04, 0x08, 0, 0},
++ {0x3f05, 0xa6, 0, 0}, // hts - { 0x24
++
++ {0x3235, 0x04, 0, 0},
++ {0x3236, 0xae, 0, 0}, // vts-2
++
++ //0825
++ {0x3313, 0x05, 0, 0},
++ {0x3678, 0x42, 0, 0},
++
++ //for AE control per frame
++ {0x3670, 0x00, 0, 0},
++ {0x3633, 0x42, 0, 0},
++
++ {0x3802, 0x00, 0, 0},
++
++ //20180126
++ {0x3677, 0x3f, 0, 0},
++ {0x3306, 0x44, 0, 0}, //20180126[3c },4a]
++ {0x330b, 0xca, 0, 0}, //20180126[c2 },d3]
++
++ //20180202
++ {0x3237, 0x08, 0, 0},
++ {0x3238, 0x9a, 0, 0}, //hts-0x30
++
++ //20180417
++ {0x3640, 0x01, 0, 0},
++ {0x3641, 0x02, 0, 0},
++
++ {0x3301, 0x12, 0, 0}, //[8 },15]20180126
++ {0x3631, 0x84, 0, 0},
++ {0x366f, 0x2f, 0, 0},
++ {0x3622, 0xc6, 0, 0}, //20180117
++
++ {0x3e03, 0x03, 0, 0}, // Bit[3]: AGC table mapping method, Bit[1]: AGC manual, BIt[0]: AEC manual
++
++ // {0x0100, 0x00, 0, 0},
++ // {0x4501, 0xc8, 0, 0}, //bar testing
++ // {0x3902, 0x45, 0, 0},
++};
++
++static struct reg_value sc2235_setting_1080P_1920_1080[] = {
++
++};
++
++/* power-on sensor init reg table */
++static const struct sc2235_mode_info sc2235_mode_init_data = {
++ SC2235_MODE_1080P_1920_1080,
++ 1920, 0x8ca, 1080, 0x4b0,
++ sc2235_init_regs_tbl_1080,
++ ARRAY_SIZE(sc2235_init_regs_tbl_1080),
++ SC2235_30_FPS,
++};
++
++static const struct sc2235_mode_info
++sc2235_mode_data[SC2235_NUM_MODES] = {
++ {SC2235_MODE_1080P_1920_1080,
++ 1920, 0x8ca, 1080, 0x4b0,
++ sc2235_setting_1080P_1920_1080,
++ ARRAY_SIZE(sc2235_setting_1080P_1920_1080),
++ SC2235_30_FPS},
++};
++
++static int sc2235_write_reg(struct sc2235_dev *sensor, u16 reg, u8 val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg;
++ u8 buf[3];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val;
++
++ msg.addr = client->addr;
++ msg.flags = client->flags;
++ msg.buf = buf;
++ msg.len = sizeof(buf);
++
++ ret = i2c_transfer(client->adapter, &msg, 1);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++ __func__, reg, val);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int sc2235_read_reg(struct sc2235_dev *sensor, u16 reg, u8 *val)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ struct i2c_msg msg[2];
++ u8 buf[2];
++ int ret;
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++
++ msg[0].addr = client->addr;
++ msg[0].flags = client->flags;
++ msg[0].buf = buf;
++ msg[0].len = sizeof(buf);
++
++ msg[1].addr = client->addr;
++ msg[1].flags = client->flags | I2C_M_RD;
++ msg[1].buf = buf;
++ msg[1].len = 1;
++
++ ret = i2c_transfer(client->adapter, msg, 2);
++ if (ret < 0) {
++ dev_err(&client->dev, "%s: error: reg=%x\n",
++ __func__, reg);
++ return ret;
++ }
++
++ *val = buf[0];
++ return 0;
++}
++
++static int sc2235_read_reg16(struct sc2235_dev *sensor, u16 reg, u16 *val)
++{
++ u8 hi, lo;
++ int ret;
++
++ ret = sc2235_read_reg(sensor, reg, &hi);
++ if (ret)
++ return ret;
++ ret = sc2235_read_reg(sensor, reg + 1, &lo);
++ if (ret)
++ return ret;
++
++ *val = ((u16)hi << 8) | (u16)lo;
++ return 0;
++}
++
++static int sc2235_write_reg16(struct sc2235_dev *sensor, u16 reg, u16 val)
++{
++ int ret;
++
++ ret = sc2235_write_reg(sensor, reg, val >> 8);
++ if (ret)
++ return ret;
++
++ return sc2235_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int sc2235_mod_reg(struct sc2235_dev *sensor, u16 reg,
++ u8 mask, u8 val)
++{
++ u8 readval;
++ int ret;
++
++ ret = sc2235_read_reg(sensor, reg, &readval);
++ if (ret)
++ return ret;
++
++ readval &= ~mask;
++ val &= mask;
++ val |= readval;
++
++ return sc2235_write_reg(sensor, reg, val);
++}
++
++#define SC2235_PLL_PREDIV 3
++
++#define SC2235_SYSDIV_MIN 0
++#define SC2235_SYSDIV_MAX 7
++
++#define SC2235_PLL_MULT_MIN 0
++#define SC2235_PLL_MULT_MAX 63
++
++#ifdef UNUSED_CODE
++static unsigned long sc2235_compute_sys_clk(struct sc2235_dev *sensor,
++ u8 pll_pre, u8 pll_mult,
++ u8 sysdiv)
++{
++ unsigned long sysclk =
++ sensor->xclk_freq * (64 - pll_mult) / (pll_pre * (sysdiv + 1));
++
++ /* PLL1 output cannot exceed 1GHz. */
++ if (sysclk / 1000000 > 1000)
++ return 0;
++
++ return sysclk;
++}
++
++static unsigned long sc2235_calc_sys_clk(struct sc2235_dev *sensor,
++ unsigned long rate,
++ u8 *pll_prediv, u8 *pll_mult,
++ u8 *sysdiv)
++{
++ unsigned long best = ~0;
++ u8 best_sysdiv = 1, best_mult = 1;
++ u8 _sysdiv, _pll_mult;
++
++ for (_sysdiv = SC2235_SYSDIV_MIN;
++ _sysdiv <= SC2235_SYSDIV_MAX;
++ _sysdiv++) {
++ for (_pll_mult = SC2235_PLL_MULT_MIN;
++ _pll_mult <= SC2235_PLL_MULT_MAX;
++ _pll_mult++) {
++ unsigned long _rate;
++
++ _rate = sc2235_compute_sys_clk(sensor,
++ SC2235_PLL_PREDIV,
++ _pll_mult, _sysdiv);
++
++ /*
++ * We have reached the maximum allowed PLL1 output,
++ * increase sysdiv.
++ */
++ if (!_rate)
++ break;
++
++ /*
++ * Prefer rates above the expected clock rate than
++ * below, even if that means being less precise.
++ */
++ if (_rate < rate)
++ continue;
++
++ if (abs(rate - _rate) < abs(rate - best)) {
++ best = _rate;
++ best_sysdiv = _sysdiv;
++ best_mult = _pll_mult;
++ }
++
++ if (_rate == rate)
++ goto out;
++ }
++ }
++
++out:
++ *sysdiv = best_sysdiv;
++ *pll_prediv = SC2235_PLL_PREDIV;
++ *pll_mult = best_mult;
++
++ return best;
++}
++#endif
++
++static int sc2235_set_timings(struct sc2235_dev *sensor,
++ const struct sc2235_mode_info *mode)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int sc2235_load_regs(struct sc2235_dev *sensor,
++ const struct sc2235_mode_info *mode)
++{
++ const struct reg_value *regs = mode->reg_data;
++ unsigned int i;
++ u32 delay_ms;
++ u16 reg_addr;
++ u8 mask, val;
++ int ret = 0;
++
++ for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++ delay_ms = regs->delay_ms;
++ reg_addr = regs->reg_addr;
++ val = regs->val;
++ mask = regs->mask;
++
++ if (mask)
++ ret = sc2235_mod_reg(sensor, reg_addr, mask, val);
++ else
++ ret = sc2235_write_reg(sensor, reg_addr, val);
++ if (ret)
++ break;
++
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++
++ return sc2235_set_timings(sensor, mode);
++}
++
++static int sc2235_set_autoexposure(struct sc2235_dev *sensor, bool on)
++{
++ return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL,
++ BIT(0), on ? 0 : BIT(0));
++}
++
++static int sc2235_get_exposure(struct sc2235_dev *sensor)
++{
++ int exp = 0, ret = 0;
++ u8 temp;
++
++ ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_HI, &temp);
++ if (ret)
++ return ret;
++ exp |= (int)temp << 8;
++ ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_LO, &temp);
++ if (ret)
++ return ret;
++ exp |= (int)temp;
++
++ return exp >> 4;
++}
++
++static int sc2235_set_exposure(struct sc2235_dev *sensor, u32 exposure)
++{
++ int ret;
++
++ exposure <<= 4;
++
++ ret = sc2235_write_reg(sensor,
++ SC2235_REG_AEC_PK_EXPOSURE_LO,
++ exposure & 0xff);
++ if (ret)
++ return ret;
++ return sc2235_write_reg(sensor,
++ SC2235_REG_AEC_PK_EXPOSURE_HI,
++ (exposure >> 8) & 0xff);
++}
++
++static int sc2235_get_gain(struct sc2235_dev *sensor)
++{
++ u16 gain;
++ int ret;
++
++ ret = sc2235_read_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN, &gain);
++ if (ret)
++ return ret;
++
++ return gain & 0x1fff;
++}
++
++static int sc2235_set_gain(struct sc2235_dev *sensor, int gain)
++{
++ return sc2235_write_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN,
++ (u16)gain & 0x1fff);
++}
++
++static int sc2235_set_autogain(struct sc2235_dev *sensor, bool on)
++{
++ return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL,
++ BIT(1), on ? 0 : BIT(1));
++}
++
++#ifdef UNUSED_CODE
++static int sc2235_get_sysclk(struct sc2235_dev *sensor)
++{
++ return 0;
++}
++
++static int sc2235_set_night_mode(struct sc2235_dev *sensor)
++{
++ return 0;
++}
++
++static int sc2235_get_hts(struct sc2235_dev *sensor)
++{
++ u16 hts;
++ int ret;
++
++ ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_HTS, &hts);
++ if (ret)
++ return ret;
++ return hts;
++}
++#endif
++
++static int sc2235_get_vts(struct sc2235_dev *sensor)
++{
++ u16 vts;
++ int ret;
++
++ ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_VTS, &vts);
++ if (ret)
++ return ret;
++ return vts;
++}
++
++#ifdef UNUSED_CODE
++static int sc2235_set_vts(struct sc2235_dev *sensor, int vts)
++{
++ return sc2235_write_reg16(sensor, SC2235_REG_TIMING_VTS, vts);
++}
++
++static int sc2235_get_light_freq(struct sc2235_dev *sensor)
++{
++ return 0;
++}
++
++static int sc2235_set_bandingfilter(struct sc2235_dev *sensor)
++{
++ return 0;
++}
++
++static int sc2235_set_ae_target(struct sc2235_dev *sensor, int target)
++{
++ return 0;
++}
++
++static int sc2235_get_binning(struct sc2235_dev *sensor)
++{
++ return 0;
++}
++
++static int sc2235_set_binning(struct sc2235_dev *sensor, bool enable)
++{
++ return 0;
++}
++
++#endif
++
++static const struct sc2235_mode_info *
++sc2235_find_mode(struct sc2235_dev *sensor, enum sc2235_frame_rate fr,
++ int width, int height, bool nearest)
++{
++ const struct sc2235_mode_info *mode;
++
++ mode = v4l2_find_nearest_size(sc2235_mode_data,
++ ARRAY_SIZE(sc2235_mode_data),
++ hact, vact,
++ width, height);
++
++ if (!mode ||
++ (!nearest && (mode->hact != width || mode->vact != height)))
++ return NULL;
++
++ /* Check to see if the current mode exceeds the max frame rate */
++ if (sc2235_framerates[fr] > sc2235_framerates[mode->max_fps])
++ return NULL;
++
++ return mode;
++}
++
++static u64 sc2235_calc_pixel_rate(struct sc2235_dev *sensor)
++{
++ u64 rate;
++
++ rate = sensor->current_mode->vtot * sensor->current_mode->htot;
++ rate *= sc2235_framerates[sensor->current_fr];
++
++ return rate;
++}
++
++#ifdef UNUSED_CODE
++/*
++ * sc2235_set_dvp_pclk() - Calculate the clock tree configuration values
++ * for the dvp output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ * 'Bandwidth Per Lane' is calculated as:
++ * rate = HTOT * VTOT * FPS;
++ *
++ * This function use the requested bandwidth to calculate:
++ * - rate = xclk * (64 - M) / (N * (S + 1));
++ *
++ */
++
++#define PLL_PREDIV 1
++#define PLL_SYSEL 0
++
++static int sc2235_set_dvp_pclk(struct sc2235_dev *sensor,
++ unsigned long rate)
++{
++ u8 prediv, mult, sysdiv;
++ int ret = 0;
++
++ sc2235_calc_sys_clk(sensor, rate, &prediv, &mult,
++ &sysdiv);
++
++
++ return ret;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int sc2235_set_mode_direct(struct sc2235_dev *sensor,
++ const struct sc2235_mode_info *mode)
++{
++ if (!mode->reg_data)
++ return -EINVAL;
++
++ /* Write capture setting */
++ return sc2235_load_regs(sensor, mode);
++}
++#endif
++
++static int sc2235_set_mode(struct sc2235_dev *sensor)
++{
++#ifdef UNUSED_CODE
++ bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
++ const struct sc2235_mode_info *mode = sensor->current_mode;
++#endif
++ bool auto_gain = sensor->ctrls.auto_gain->val == 1;
++ int ret = 0;
++
++ /* auto gain and exposure must be turned off when changing modes */
++ if (auto_gain) {
++ ret = sc2235_set_autogain(sensor, false);
++ if (ret)
++ return ret;
++ }
++#ifdef UNUSED_CODE
++ /* This issue will be addressed in the EVB board*/
++ /* This action will result in poor image display 2021 1111*/
++ if (auto_exp) {
++ ret = sc2235_set_autoexposure(sensor, false);
++ if (ret)
++ goto restore_auto_gain;
++ }
++
++ rate = sc2235_calc_pixel_rate(sensor);
++
++ ret = sc2235_set_dvp_pclk(sensor, rate);
++ if (ret < 0)
++ return 0;
++
++ ret = sc2235_set_mode_direct(sensor, mode);
++ if (ret < 0)
++ goto restore_auto_exp_gain;
++
++ /* restore auto gain and exposure */
++ if (auto_gain)
++ sc2235_set_autogain(sensor, true);
++ if (auto_exp)
++ sc2235_set_autoexposure(sensor, true);
++
++
++ sensor->pending_mode_change = false;
++ sensor->last_mode = mode;
++ return 0;
++
++restore_auto_exp_gain:
++ if (auto_exp)
++ sc2235_set_autoexposure(sensor, true);
++restore_auto_gain:
++ if (auto_gain)
++ sc2235_set_autogain(sensor, true);
++#endif
++ return ret;
++}
++
++static int sc2235_set_framefmt(struct sc2235_dev *sensor,
++ struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int sc2235_restore_mode(struct sc2235_dev *sensor)
++{
++ int ret;
++
++ /* first load the initial register values */
++ ret = sc2235_load_regs(sensor, &sc2235_mode_init_data);
++ if (ret < 0)
++ return ret;
++ sensor->last_mode = &sc2235_mode_init_data;
++ /* now restore the last capture mode */
++ ret = sc2235_set_mode(sensor);
++ if (ret < 0)
++ return ret;
++
++ return sc2235_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void sc2235_power(struct sc2235_dev *sensor, bool enable)
++{
++ if (!sensor->pwdn_gpio)
++ return;
++ gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void sc2235_reset(struct sc2235_dev *sensor)
++{
++ if (!sensor->reset_gpio)
++ return;
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++ /* camera power cycle */
++ sc2235_power(sensor, false);
++ usleep_range(5000, 10000);
++ sc2235_power(sensor, true);
++ usleep_range(5000, 10000);
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++ usleep_range(1000, 2000);
++
++ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++ usleep_range(20000, 25000);
++}
++
++static int sc2235_set_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ int ret;
++
++ ret = clk_prepare_enable(sensor->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ return ret;
++ }
++
++ ret = regulator_bulk_enable(SC2235_NUM_SUPPLIES,
++ sensor->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ goto xclk_off;
++ }
++
++ sc2235_reset(sensor);
++ sc2235_power(sensor, true);
++
++ return 0;
++
++xclk_off:
++ clk_disable_unprepare(sensor->xclk);
++ return ret;
++}
++
++static int sc2235_set_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++ sc2235_power(sensor, false);
++ regulator_bulk_disable(SC2235_NUM_SUPPLIES, sensor->supplies);
++ clk_disable_unprepare(sensor->xclk);
++
++ return 0;
++}
++
++static int sc2235_set_power(struct sc2235_dev *sensor, bool on)
++{
++ int ret = 0;
++
++ if (on) {
++ pm_runtime_get_sync(&sensor->i2c_client->dev);
++
++ ret = sc2235_restore_mode(sensor);
++ if (ret)
++ goto power_off;
++ }
++
++ if (!on)
++ pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++ return 0;
++
++power_off:
++ pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++ return ret;
++}
++
++static int sc2235_s_power(struct v4l2_subdev *sd, int on)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ int ret = 0;
++
++ mutex_lock(&sensor->lock);
++
++ ret = sc2235_set_power(sensor, !!on);
++ if (ret)
++ goto out;
++
++ mutex_unlock(&sensor->lock);
++ return 0;
++
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int sc2235_try_frame_interval(struct sc2235_dev *sensor,
++ struct v4l2_fract *fi,
++ u32 width, u32 height)
++{
++ const struct sc2235_mode_info *mode;
++ enum sc2235_frame_rate rate = SC2235_15_FPS;
++ int minfps, maxfps, best_fps, fps;
++ int i;
++
++ minfps = sc2235_framerates[SC2235_15_FPS];
++ maxfps = sc2235_framerates[SC2235_30_FPS];
++
++ if (fi->numerator == 0) {
++ fi->denominator = maxfps;
++ fi->numerator = 1;
++ rate = SC2235_30_FPS;
++ goto find_mode;
++ }
++
++ fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++ minfps, maxfps);
++
++ best_fps = minfps;
++ for (i = 0; i < ARRAY_SIZE(sc2235_framerates); i++) {
++ int curr_fps = sc2235_framerates[i];
++
++ if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++ best_fps = curr_fps;
++ rate = i;
++ }
++ }
++
++ fi->numerator = 1;
++ fi->denominator = best_fps;
++
++find_mode:
++ mode = sc2235_find_mode(sensor, rate, width, height, false);
++ return mode ? rate : -EINVAL;
++}
++
++static int sc2235_get_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ struct v4l2_mbus_framefmt *fmt;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++ format->pad);
++ else
++ fmt = &sensor->fmt;
++
++ format->format = *fmt;
++
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int sc2235_try_fmt_internal(struct v4l2_subdev *sd,
++ struct v4l2_mbus_framefmt *fmt,
++ enum sc2235_frame_rate fr,
++ const struct sc2235_mode_info **new_mode)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ const struct sc2235_mode_info *mode;
++ int i;
++
++ mode = sc2235_find_mode(sensor, fr, fmt->width, fmt->height, true);
++ if (!mode)
++ return -EINVAL;
++ fmt->width = mode->hact;
++ fmt->height = mode->vact;
++
++ if (new_mode)
++ *new_mode = mode;
++
++ for (i = 0; i < ARRAY_SIZE(sc2235_formats); i++)
++ if (sc2235_formats[i].code == fmt->code)
++ break;
++ if (i >= ARRAY_SIZE(sc2235_formats))
++ i = 0;
++
++ fmt->code = sc2235_formats[i].code;
++ fmt->colorspace = sc2235_formats[i].colorspace;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++ return 0;
++}
++
++static int sc2235_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ const struct sc2235_mode_info *new_mode;
++ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++ struct v4l2_mbus_framefmt *fmt;
++ int ret;
++
++ if (format->pad != 0)
++ return -EINVAL;
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ ret = sc2235_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++ if (ret)
++ goto out;
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ fmt = v4l2_subdev_get_try_format(sd, state, 0);
++ else
++ fmt = &sensor->fmt;
++
++ if (mbus_fmt->code != sensor->fmt.code)
++ sensor->pending_fmt_change = true;
++
++ *fmt = *mbus_fmt;
++
++ if (new_mode != sensor->current_mode) {
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++ if (new_mode->max_fps < sensor->current_fr) {
++ sensor->current_fr = new_mode->max_fps;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator =
++ sc2235_framerates[sensor->current_fr];
++ sensor->current_mode = new_mode;
++ sensor->pending_mode_change = true;
++ }
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ sc2235_calc_pixel_rate(sensor));
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int sc2235_set_framefmt(struct sc2235_dev *sensor,
++ struct v4l2_mbus_framefmt *format)
++{
++ int ret = 0;
++
++ switch (format->code) {
++ default:
++ return ret;
++ }
++ return ret;
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int sc2235_set_ctrl_hue(struct sc2235_dev *sensor, int value)
++{
++ int ret = 0;
++ return ret;
++}
++
++static int sc2235_set_ctrl_contrast(struct sc2235_dev *sensor, int value)
++{
++ int ret = 0;
++ return ret;
++}
++
++static int sc2235_set_ctrl_saturation(struct sc2235_dev *sensor, int value)
++{
++ int ret = 0;
++ return ret;
++}
++
++static int sc2235_set_ctrl_white_balance(struct sc2235_dev *sensor, int awb)
++{
++ int ret = 0;
++ return ret;
++}
++
++static int sc2235_set_ctrl_exposure(struct sc2235_dev *sensor,
++ enum v4l2_exposure_auto_type auto_exposure)
++{
++ struct sc2235_ctrls *ctrls = &sensor->ctrls;
++ bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++ int ret = 0;
++
++ if (ctrls->auto_exp->is_new) {
++ ret = sc2235_set_autoexposure(sensor, auto_exp);
++ if (ret)
++ return ret;
++ }
++
++ if (!auto_exp && ctrls->exposure->is_new) {
++ u16 max_exp = 0;
++
++ ret = sc2235_get_vts(sensor);
++ if (ret < 0)
++ return ret;
++ max_exp += ret - 4;
++ ret = 0;
++
++ if (ctrls->exposure->val < max_exp)
++ ret = sc2235_set_exposure(sensor, ctrls->exposure->val);
++ }
++
++ return ret;
++}
++
++static int sc2235_set_ctrl_gain(struct sc2235_dev *sensor, bool auto_gain)
++{
++ struct sc2235_ctrls *ctrls = &sensor->ctrls;
++ int ret = 0;
++
++ if (ctrls->auto_gain->is_new) {
++ ret = sc2235_set_autogain(sensor, auto_gain);
++ if (ret)
++ return ret;
++ }
++
++ if (!auto_gain && ctrls->gain->is_new)
++ ret = sc2235_set_gain(sensor, ctrls->gain->val);
++
++ return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++ "Disabled",
++ "Black bars",
++ "Auto Black bars",
++};
++
++#define SC2235_TEST_ENABLE BIT(3)
++#define SC2235_TEST_BLACK (3 << 0)
++
++static int sc2235_set_ctrl_test_pattern(struct sc2235_dev *sensor, int value)
++{
++ int ret = 0;
++ /*
++ *For 7110 platform, refer to 1125 FW code configuration. This operation will cause the image to be white.
++ */
++#ifdef UNUSED_CODE
++ ret = sc2235_mod_reg(sensor, SC2235_REG_TEST_SET0, BIT(3),
++ !!value << 3);
++
++ ret |= sc2235_mod_reg(sensor, SC2235_REG_TEST_SET1, BIT(6),
++ (value >> 1) << 6);
++#endif
++ return ret;
++}
++
++static int sc2235_set_ctrl_light_freq(struct sc2235_dev *sensor, int value)
++{
++ return 0;
++}
++
++static int sc2235_set_ctrl_hflip(struct sc2235_dev *sensor, int value)
++{
++ return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21,
++ BIT(2) | BIT(1),
++ (!(value ^ sensor->upside_down)) ?
++ (BIT(2) | BIT(1)) : 0);
++}
++
++static int sc2235_set_ctrl_vflip(struct sc2235_dev *sensor, int value)
++{
++ return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21,
++ BIT(6) | BIT(5),
++ (value ^ sensor->upside_down) ?
++ (BIT(6) | BIT(5)) : 0);
++}
++
++static int sc2235_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ int val;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ val = sc2235_get_gain(sensor);
++ if (val < 0)
++ return val;
++ sensor->ctrls.gain->val = val;
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ val = sc2235_get_exposure(sensor);
++ if (val < 0)
++ return val;
++ sensor->ctrls.exposure->val = val;
++ break;
++ }
++
++ pm_runtime_put(&sensor->i2c_client->dev);
++
++ return 0;
++}
++
++static int sc2235_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ int ret;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored at start streaming time.
++ */
++ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ ret = sc2235_set_ctrl_gain(sensor, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ ret = sc2235_set_ctrl_exposure(sensor, ctrl->val);
++ break;
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = sc2235_set_ctrl_white_balance(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HUE:
++ ret = sc2235_set_ctrl_hue(sensor, ctrl->val);
++ break;
++ case V4L2_CID_CONTRAST:
++ ret = sc2235_set_ctrl_contrast(sensor, ctrl->val);
++ break;
++ case V4L2_CID_SATURATION:
++ ret = sc2235_set_ctrl_saturation(sensor, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = sc2235_set_ctrl_test_pattern(sensor, ctrl->val);
++ break;
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ ret = sc2235_set_ctrl_light_freq(sensor, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = sc2235_set_ctrl_hflip(sensor, ctrl->val);
++ break;
++ case V4L2_CID_VFLIP:
++ ret = sc2235_set_ctrl_vflip(sensor, ctrl->val);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_put(&sensor->i2c_client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops sc2235_ctrl_ops = {
++ .g_volatile_ctrl = sc2235_g_volatile_ctrl,
++ .s_ctrl = sc2235_s_ctrl,
++};
++
++static int sc2235_init_controls(struct sc2235_dev *sensor)
++{
++ const struct v4l2_ctrl_ops *ops = &sc2235_ctrl_ops;
++ struct sc2235_ctrls *ctrls = &sensor->ctrls;
++ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++ int ret;
++
++ v4l2_ctrl_handler_init(hdl, 32);
++
++ /* we can use our own mutex for the ctrl lock */
++ hdl->lock = &sensor->lock;
++
++ /* Clock related controls */
++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++ 0, INT_MAX, 1,
++ sc2235_calc_pixel_rate(sensor));
++
++ /* Auto/manual white balance */
++ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, 1, 1, 1);
++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++ 0, 4095, 1, 0);
++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++ 0, 4095, 1, 0);
++ /* Auto/manual exposure */
++#ifdef UNUSED_CODE
++ /*
++ *For 7110 platform, This operation will cause the image to be white.
++ */
++ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_EXPOSURE_AUTO,
++ V4L2_EXPOSURE_MANUAL, 0,
++ V4L2_EXPOSURE_AUTO);
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 0, 65535, 1, 0);
++ /* Auto/manual gain */
++ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++ 0, 1, 1, 1);
++ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++ 0, 1023, 1, 0);
++#else
++ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_EXPOSURE_AUTO,
++ V4L2_EXPOSURE_MANUAL, 0,
++ 1);
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 0, 65535, 1, 720);
++ /* Auto/manual gain */
++ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++ 0, 1, 1, 0);
++ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++ 0, 1023, 1, 0x10);
++#endif
++ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++ 0, 255, 1, 64);
++ ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++ 0, 359, 1, 0);
++ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++ 0, 255, 1, 0);
++ ctrls->test_pattern =
++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(test_pattern_menu) - 1,
++ 0, 0, test_pattern_menu); //0x02
++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++ 0, 1, 1, 1);
++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++ 0, 1, 1, 0);
++
++ ctrls->light_freq =
++ v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_POWER_LINE_FREQUENCY,
++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++ if (hdl->error) {
++ ret = hdl->error;
++ goto free_ctrls;
++ }
++
++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++ sensor->sd.ctrl_handler = hdl;
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(hdl);
++ return ret;
++}
++
++static int sc2235_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ if (fse->pad != 0)
++ return -EINVAL;
++ if (fse->index >= SC2235_NUM_MODES)
++ return -EINVAL;
++
++ fse->min_width =
++ sc2235_mode_data[fse->index].hact;
++ fse->max_width = fse->min_width;
++ fse->min_height =
++ sc2235_mode_data[fse->index].vact;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int sc2235_enum_frame_interval(
++ struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_interval_enum *fie)
++{
++ struct v4l2_fract tpf;
++ int i;
++
++ if (fie->pad != 0)
++ return -EINVAL;
++
++ if (fie->index >= SC2235_NUM_FRAMERATES)
++ return -EINVAL;
++
++ tpf.numerator = 1;
++ tpf.denominator = sc2235_framerates[fie->index];
++
++ for (i = 0; i < SC2235_NUM_MODES; i++) {
++ if (fie->width == sc2235_mode_data[i].hact &&
++ fie->height == sc2235_mode_data[i].vact)
++ break;
++ }
++ if (i == SC2235_NUM_MODES)
++ return -ENOTTY;
++
++ fie->interval = tpf;
++ return 0;
++}
++
++static int sc2235_g_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++ mutex_lock(&sensor->lock);
++ fi->interval = sensor->frame_interval;
++ mutex_unlock(&sensor->lock);
++
++ return 0;
++}
++
++static int sc2235_s_frame_interval(struct v4l2_subdev *sd,
++ struct v4l2_subdev_frame_interval *fi)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ const struct sc2235_mode_info *mode;
++ int frame_rate, ret = 0;
++
++ if (fi->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ mode = sensor->current_mode;
++
++ frame_rate = sc2235_try_frame_interval(sensor, &fi->interval,
++ mode->hact, mode->vact);
++ if (frame_rate < 0) {
++ /* Always return a valid frame interval value */
++ fi->interval = sensor->frame_interval;
++ goto out;
++ }
++
++ mode = sc2235_find_mode(sensor, frame_rate, mode->hact,
++ mode->vact, true);
++ if (!mode) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (mode != sensor->current_mode ||
++ frame_rate != sensor->current_fr) {
++ sensor->current_fr = frame_rate;
++ sensor->frame_interval = fi->interval;
++ sensor->current_mode = mode;
++ sensor->pending_mode_change = true;
++
++ __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++ sc2235_calc_pixel_rate(sensor));
++ }
++out:
++ mutex_unlock(&sensor->lock);
++ return ret;
++}
++
++static int sc2235_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ if (code->pad != 0)
++ return -EINVAL;
++ if (code->index >= ARRAY_SIZE(sc2235_formats))
++ return -EINVAL;
++
++ code->code = sc2235_formats[code->index].code;
++ return 0;
++}
++
++static int sc2235_stream_start(struct sc2235_dev *sensor, int enable)
++{
++ return sc2235_mod_reg(sensor, SC2235_REG_STREAM_ON, BIT(0), !!enable);
++}
++
++static int sc2235_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++ int ret = 0;
++
++ if (enable) {
++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++ if (ret)
++ return ret;
++ }
++
++ mutex_lock(&sensor->lock);
++
++ if (sensor->streaming == !enable) {
++ if (enable && sensor->pending_mode_change) {
++ ret = sc2235_set_mode(sensor);
++ if (ret)
++ goto out;
++ }
++
++ if (enable && sensor->pending_fmt_change) {
++ ret = sc2235_set_framefmt(sensor, &sensor->fmt);
++ if (ret)
++ goto out;
++ sensor->pending_fmt_change = false;
++ }
++
++ ret = sc2235_stream_start(sensor, enable);
++ if (ret)
++ goto out;
++ }
++ sensor->streaming += enable ? 1 : -1;
++ WARN_ON(sensor->streaming < 0);
++out:
++ mutex_unlock(&sensor->lock);
++
++ return ret;
++}
++
++static const struct v4l2_subdev_core_ops sc2235_core_ops = {
++ .s_power = sc2235_s_power,
++ .log_status = v4l2_ctrl_subdev_log_status,
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops sc2235_video_ops = {
++ .g_frame_interval = sc2235_g_frame_interval,
++ .s_frame_interval = sc2235_s_frame_interval,
++ .s_stream = sc2235_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops sc2235_pad_ops = {
++ .enum_mbus_code = sc2235_enum_mbus_code,
++ .get_fmt = sc2235_get_fmt,
++ .set_fmt = sc2235_set_fmt,
++ .enum_frame_size = sc2235_enum_frame_size,
++ .enum_frame_interval = sc2235_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops sc2235_subdev_ops = {
++ .core = &sc2235_core_ops,
++ .video = &sc2235_video_ops,
++ .pad = &sc2235_pad_ops,
++};
++
++static int sc2235_get_regulators(struct sc2235_dev *sensor)
++{
++ int i;
++
++ for (i = 0; i < SC2235_NUM_SUPPLIES; i++)
++ sensor->supplies[i].supply = sc2235_supply_name[i];
++
++ return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++ SC2235_NUM_SUPPLIES,
++ sensor->supplies);
++}
++
++static int sc2235_check_chip_id(struct sc2235_dev *sensor)
++{
++ struct i2c_client *client = sensor->i2c_client;
++ int ret = 0;
++ u16 chip_id;
++
++ ret = sc2235_read_reg16(sensor, SC2235_REG_CHIP_ID, &chip_id);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to read chip identifier\n",
++ __func__);
++ return ret;
++ }
++
++ if (chip_id != SC2235_CHIP_ID) {
++ dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++ __func__, SC2235_CHIP_ID, chip_id);
++ return -ENXIO;
++ }
++ dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++ __func__, chip_id);
++
++ return 0;
++}
++
++static int sc2235_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct fwnode_handle *endpoint;
++ struct sc2235_dev *sensor;
++ struct v4l2_mbus_framefmt *fmt;
++ u32 rotation;
++ int ret;
++
++ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++ if (!sensor)
++ return -ENOMEM;
++
++ sensor->i2c_client = client;
++
++ fmt = &sensor->fmt;
++ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = 1920;
++ fmt->height = 1080;
++ fmt->field = V4L2_FIELD_NONE;
++ sensor->frame_interval.numerator = 1;
++ sensor->frame_interval.denominator = sc2235_framerates[SC2235_30_FPS];
++ sensor->current_fr = SC2235_30_FPS;
++ sensor->current_mode =
++ &sc2235_mode_data[SC2235_MODE_1080P_1920_1080];
++ sensor->last_mode = sensor->current_mode;
++
++ /* optional indication of physical rotation of sensor */
++ ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++ &rotation);
++ if (!ret) {
++ switch (rotation) {
++ case 180:
++ sensor->upside_down = true;
++ fallthrough;
++ case 0:
++ break;
++ default:
++ dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++ rotation);
++ }
++ }
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++ NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++ fwnode_handle_put(endpoint);
++ if (ret) {
++ dev_err(dev, "Could not parse endpoint\n");
++ return ret;
++ }
++
++ if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) {
++ dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++ return -EINVAL;
++ }
++
++ /* get system clock (xclk) */
++ sensor->xclk = devm_clk_get(dev, "xclk");
++ if (IS_ERR(sensor->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(sensor->xclk);
++ }
++
++ sensor->xclk_freq = clk_get_rate(sensor->xclk);
++ if (sensor->xclk_freq < SC2235_XCLK_MIN ||
++ sensor->xclk_freq > SC2235_XCLK_MAX) {
++ dev_err(dev, "xclk frequency out of range: %d Hz\n",
++ sensor->xclk_freq);
++ return -EINVAL;
++ }
++
++ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->pwdn_gpio))
++ return PTR_ERR(sensor->pwdn_gpio);
++
++ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++ if (IS_ERR(sensor->reset_gpio))
++ return PTR_ERR(sensor->reset_gpio);
++
++ v4l2_i2c_subdev_init(&sensor->sd, client, &sc2235_subdev_ops);
++
++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++ if (ret)
++ return ret;
++
++ ret = sc2235_get_regulators(sensor);
++ if (ret)
++ return ret;
++ mutex_init(&sensor->lock);
++
++ ret = sc2235_set_power_on(dev);
++ if (ret) {
++ dev_err(dev, "failed to power on\n");
++ goto entity_cleanup;
++ }
++
++ ret = sc2235_check_chip_id(sensor);
++ if (ret)
++ goto entity_power_off;
++
++ ret = sc2235_init_controls(sensor);
++ if (ret)
++ goto entity_power_off;
++
++ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++ if (ret)
++ goto free_ctrls;
++
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_power_off:
++ sc2235_set_power_off(dev);
++entity_cleanup:
++ media_entity_cleanup(&sensor->sd.entity);
++ mutex_destroy(&sensor->lock);
++ return ret;
++}
++
++static void sc2235_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++ v4l2_async_unregister_subdev(&sensor->sd);
++ media_entity_cleanup(&sensor->sd.entity);
++ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++ mutex_destroy(&sensor->lock);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ sc2235_set_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct i2c_device_id sc2235_id[] = {
++ { "sc2235", 0 },
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, sc2235_id);
++
++static const struct of_device_id sc2235_dt_ids[] = {
++ { .compatible = "smartsens,sc2235" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, sc2235_dt_ids);
++
++static const struct dev_pm_ops sc2235_pm_ops = {
++ SET_RUNTIME_PM_OPS(sc2235_set_power_off, sc2235_set_power_on, NULL)
++};
++
++static struct i2c_driver sc2235_i2c_driver = {
++ .driver = {
++ .name = "sc2235",
++ .of_match_table = sc2235_dt_ids,
++ .pm = &sc2235_pm_ops,
++ },
++ .id_table = sc2235_id,
++ .probe = sc2235_probe,
++ .remove = sc2235_remove,
++};
++
++module_i2c_driver(sc2235_i2c_driver);
++
++MODULE_DESCRIPTION("SC2235 Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_common.h
+@@ -0,0 +1,185 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_COMMON_H
++#define STF_COMMON_H
++
++#include <linux/kern_levels.h>
++
++// #define STF_DEBUG
++
++// #define USE_CSIDPHY_ONE_CLK_MODE 1
++
++enum {
++ ST_DVP = 0x0001,
++ ST_CSIPHY = 0x0002,
++ ST_CSI = 0x0004,
++ ST_ISP = 0x0008,
++ ST_VIN = 0x0010,
++ ST_VIDEO = 0x0020,
++ ST_CAMSS = 0x0040,
++ ST_SENSOR = 0x0080,
++};
++
++enum {
++ ST_NONE = 0x00,
++ ST_ERR = 0x01,
++ ST_WARN = 0x02,
++ ST_INFO = 0x03,
++ ST_DEBUG = 0x04,
++};
++
++extern unsigned int stdbg_level;
++extern unsigned int stdbg_mask;
++
++#define ST_MODULE2STRING(__module) ({ \
++ char *__str; \
++ \
++ switch (__module) { \
++ case ST_DVP: \
++ __str = "st_dvp"; \
++ break; \
++ case ST_CSIPHY: \
++ __str = "st_csiphy"; \
++ break; \
++ case ST_CSI: \
++ __str = "st_csi"; \
++ break; \
++ case ST_ISP: \
++ __str = "st_isp"; \
++ break; \
++ case ST_VIN: \
++ __str = "st_vin"; \
++ break; \
++ case ST_VIDEO: \
++ __str = "st_video"; \
++ break; \
++ case ST_CAMSS: \
++ __str = "st_camss"; \
++ break; \
++ case ST_SENSOR: \
++ __str = "st_sensor"; \
++ break; \
++ default: \
++ __str = "unknow"; \
++ break; \
++ } \
++ \
++ __str; \
++ })
++
++#define st_debug(module, __fmt, arg...) \
++ do { \
++ if (stdbg_level > ST_INFO) { \
++ if (stdbg_mask & module) \
++ pr_err("[%s] debug: " __fmt, \
++ ST_MODULE2STRING(module), \
++ ## arg); \
++ } \
++ } while (0)
++
++#define st_info(module, __fmt, arg...) \
++ do { \
++ if (stdbg_level > ST_WARN) { \
++ if (stdbg_mask & module) \
++ pr_err("[%s] info: " __fmt, \
++ ST_MODULE2STRING(module), \
++ ## arg); \
++ } \
++ } while (0)
++
++#define st_warn(module, __fmt, arg...) \
++ do { \
++ if (stdbg_level > ST_ERR) { \
++ if (stdbg_mask & module) \
++ pr_err("[%s] warn: " __fmt, \
++ ST_MODULE2STRING(module), \
++ ## arg); \
++ } \
++ } while (0)
++
++#define st_err(module, __fmt, arg...) \
++ do { \
++ if (stdbg_level > ST_NONE) { \
++ if (stdbg_mask & module) \
++ pr_err("[%s] error: " __fmt, \
++ ST_MODULE2STRING(module), \
++ ## arg); \
++ } \
++ } while (0)
++
++#define st_err_ratelimited(module, fmt, ...) \
++ do { \
++ static DEFINE_RATELIMIT_STATE(_rs, \
++ DEFAULT_RATELIMIT_INTERVAL, \
++ DEFAULT_RATELIMIT_BURST); \
++ if (__ratelimit(&_rs) && (stdbg_level > ST_NONE)) { \
++ if (stdbg_mask & module) \
++ pr_err("[%s] error: " fmt, \
++ ST_MODULE2STRING(module), \
++ ##__VA_ARGS__); \
++ } \
++ } while (0)
++
++#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b)))
++
++static inline u32 reg_read(void __iomem *base, u32 reg)
++{
++ return ioread32(base + reg);
++}
++
++static inline void reg_write(void __iomem *base, u32 reg, u32 val)
++{
++ iowrite32(val, base + reg);
++}
++
++static inline void reg_set_bit(void __iomem *base, u32 reg, u32 mask, u32 val)
++{
++ u32 value;
++
++ value = ioread32(base + reg) & ~mask;
++ val &= mask;
++ val |= value;
++ iowrite32(val, base + reg);
++}
++
++static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
++{
++ iowrite32(ioread32(base + reg) | mask, base + reg);
++}
++
++static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
++{
++ iowrite32(ioread32(base + reg) & ~mask, base + reg);
++}
++
++static inline void reg_set_highest_bit(void __iomem *base, u32 reg)
++{
++ u32 val;
++
++ val = ioread32(base + reg);
++ val &= ~(0x1 << 31);
++ val |= (0x1 & 0x1) << 31;
++ iowrite32(val, base + reg);
++}
++
++static inline void reg_clr_highest_bit(void __iomem *base, u32 reg)
++{
++ u32 val;
++
++ val = ioread32(base + reg);
++ val &= ~(0x1 << 31);
++ val |= (0x0 & 0x1) << 31;
++ iowrite32(val, base + reg);
++}
++
++static inline void print_reg(unsigned int module, void __iomem *base, u32 reg)
++{
++ //st_debug(module, "REG 0x%x = 0x%x\n",
++ // base + reg, ioread32(base + reg));
++}
++
++#endif /* STF_COMMON_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.c
+@@ -0,0 +1,465 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct csi_format csi_formats_sink[] = {
++ { MEDIA_BUS_FMT_UYVY8_2X8, 16},
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++/* this bpp need see csi controllor */
++static const struct csi_format csi_formats_src[] = {
++ { MEDIA_BUS_FMT_AYUV8_1X32, 32},
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 16},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 16},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 16},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 16},
++};
++
++static int csi_find_format(u32 code,
++ const struct csi_format *formats,
++ unsigned int nformats)
++{
++ int i;
++
++ for (i = 0; i < nformats; i++)
++ if (formats[i].code == code)
++ return i;
++ return -EINVAL;
++}
++
++int stf_csi_subdev_init(struct stfcamss *stfcamss)
++{
++ struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++
++ csi_dev->s_type = SENSOR_VIN;
++ csi_dev->hw_ops = &csi_ops;
++ csi_dev->stfcamss = stfcamss;
++ csi_dev->formats_sink = csi_formats_sink;
++ csi_dev->nformats_sink = ARRAY_SIZE(csi_formats_sink);
++ csi_dev->formats_src = csi_formats_src;
++ csi_dev->nformats_src = ARRAY_SIZE(csi_formats_src);
++ mutex_init(&csi_dev->stream_lock);
++ return 0;
++}
++
++static int csi_set_power(struct v4l2_subdev *sd, int on)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++
++ csi_dev->hw_ops->csi_power_on(csi_dev, (u8)on);
++ return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__csi_get_format(struct stf_csi_dev *csi_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_format(&csi_dev->subdev, state, pad);
++
++ return &csi_dev->fmt[pad];
++}
++
++static u32 code_to_data_type(int code)
++{
++ switch (code) {
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ return 0x2b;
++ case MEDIA_BUS_FMT_UYVY8_2X8:
++ return 0x1E;
++ default:
++ return 0x2b;
++ }
++}
++
++static int csi_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++ int ret = 0;
++ u32 code, width, dt;
++ u8 bpp;
++
++ format = __csi_get_format(csi_dev, NULL, STF_CSI_PAD_SINK,
++ V4L2_SUBDEV_FORMAT_ACTIVE);
++ if (format == NULL)
++ return -EINVAL;
++
++ width = format->width;
++
++ ret = csi_find_format(format->code,
++ csi_dev->formats_sink,
++ csi_dev->nformats_sink);
++ if (ret < 0)
++ return ret;
++
++ code = csi_dev->formats_sink[ret].code;
++ bpp = csi_dev->formats_src[ret].bpp;
++ dt = code_to_data_type(code);
++
++ mutex_lock(&csi_dev->stream_lock);
++ if (enable) {
++ if (csi_dev->stream_count == 0) {
++ csi_dev->hw_ops->csi_clk_enable(csi_dev);
++ csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
++ }
++ csi_dev->stream_count++;
++ } else {
++ if (csi_dev->stream_count == 0)
++ goto exit;
++ if (csi_dev->stream_count == 1) {
++ csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
++ csi_dev->hw_ops->csi_clk_disable(csi_dev);
++ }
++ csi_dev->stream_count--;
++ }
++exit:
++ mutex_unlock(&csi_dev->stream_lock);
++ return 0;
++}
++
++static void csi_try_format(struct stf_csi_dev *csi_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ struct v4l2_mbus_framefmt *fmt,
++ enum v4l2_subdev_format_whence which)
++{
++ unsigned int i;
++
++ switch (pad) {
++ case STF_CSI_PAD_SINK:
++ /* Set format on sink pad */
++
++ for (i = 0; i < csi_dev->nformats_sink; i++)
++ if (fmt->code == csi_dev->formats_sink[i].code)
++ break;
++
++ if (i >= csi_dev->nformats_sink)
++ fmt->code = csi_dev->formats_sink[0].code;
++
++ fmt->width = clamp_t(u32,
++ fmt->width,
++ STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height,
++ STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++
++ break;
++
++ case STF_CSI_PAD_SRC:
++ /* Set format on src pad */
++
++ for (i = 0; i < csi_dev->nformats_src; i++)
++ if (fmt->code == csi_dev->formats_src[i].code)
++ break;
++
++ if (i >= csi_dev->nformats_src)
++ fmt->code = csi_dev->formats_src[0].code;
++
++ fmt->width = clamp_t(u32,
++ fmt->width,
++ STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height,
++ STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++
++ break;
++ }
++}
++
++static int csi_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++
++ if (code->index >= csi_dev->nformats_sink)
++ return -EINVAL;
++ if (code->pad == STF_CSI_PAD_SINK) {
++ code->code = csi_dev->formats_sink[code->index].code;
++ } else {
++ struct v4l2_mbus_framefmt *sink_fmt;
++
++ sink_fmt = __csi_get_format(csi_dev, state, STF_CSI_PAD_SINK,
++ code->which);
++
++ code->code = sink_fmt->code;
++ if (!code->code)
++ return -EINVAL;
++ }
++ code->flags = 0;
++
++ return 0;
++}
++
++static int csi_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt format;
++
++ if (fse->index != 0)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = 1;
++ format.height = 1;
++ csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
++ fse->min_width = format.width;
++ fse->min_height = format.height;
++
++ if (format.code != fse->code)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = -1;
++ format.height = -1;
++ csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
++ fse->max_width = format.width;
++ fse->max_height = format.height;
++
++ return 0;
++}
++
++static int csi_get_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ fmt->format = *format;
++
++ return 0;
++}
++
++static int csi_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++ struct v4l2_mbus_framefmt *format_src;
++ int ret;
++
++ format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ mutex_lock(&csi_dev->stream_lock);
++ if (csi_dev->stream_count) {
++ fmt->format = *format;
++ mutex_unlock(&csi_dev->stream_lock);
++ goto out;
++ } else {
++ csi_try_format(csi_dev, state, fmt->pad, &fmt->format, fmt->which);
++ *format = fmt->format;
++ }
++ mutex_unlock(&csi_dev->stream_lock);
++
++ if (fmt->pad == STF_CSI_PAD_SINK) {
++ format_src = __csi_get_format(csi_dev, state, STF_DVP_PAD_SRC,
++ fmt->which);
++
++ ret = csi_find_format(format->code, csi_dev->formats_sink,
++ csi_dev->nformats_sink);
++ if (ret < 0)
++ return ret;
++
++ format_src->code = csi_dev->formats_src[ret].code;
++ csi_try_format(csi_dev, state, STF_DVP_PAD_SRC, format_src,
++ fmt->which);
++ }
++out:
++ return 0;
++}
++
++static int csi_init_formats(struct v4l2_subdev *sd,
++ struct v4l2_subdev_fh *fh)
++{
++ struct v4l2_subdev_format format = {
++ .pad = STF_CSI_PAD_SINK,
++ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++ V4L2_SUBDEV_FORMAT_ACTIVE,
++ .format = {
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .width = 1920,
++ .height = 1080
++ }
++ };
++
++ return csi_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int csi_link_setup(struct media_entity *entity,
++ const struct media_pad *local,
++ const struct media_pad *remote, u32 flags)
++{
++ if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++ (flags & MEDIA_LNK_FL_ENABLED)) {
++ struct v4l2_subdev *sd;
++ struct stf_csi_dev *csi_dev;
++ struct vin_line *line;
++
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++
++ sd = media_entity_to_v4l2_subdev(entity);
++ csi_dev = v4l2_get_subdevdata(sd);
++
++ sd = media_entity_to_v4l2_subdev(remote->entity);
++ line = v4l2_get_subdevdata(sd);
++ if (line->sdev_type == VIN_DEV_TYPE)
++ csi_dev->s_type = SENSOR_VIN;
++ if (line->sdev_type == ISP_DEV_TYPE)
++ csi_dev->s_type = SENSOR_ISP;
++ st_info(ST_CSI, "CSI device sensor type: %d\n", csi_dev->s_type);
++ }
++
++ if ((local->flags & MEDIA_PAD_FL_SINK) &&
++ (flags & MEDIA_LNK_FL_ENABLED)) {
++ struct v4l2_subdev *sd;
++ struct stf_csi_dev *csi_dev;
++ struct stf_csiphy_dev *csiphy_dev;
++
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++
++ sd = media_entity_to_v4l2_subdev(entity);
++ csi_dev = v4l2_get_subdevdata(sd);
++
++ sd = media_entity_to_v4l2_subdev(remote->entity);
++ csiphy_dev = v4l2_get_subdevdata(sd);
++
++ st_info(ST_CSI, "CSI0 link to csiphy0\n");
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops csi_core_ops = {
++ .s_power = csi_set_power,
++};
++
++static const struct v4l2_subdev_video_ops csi_video_ops = {
++ .s_stream = csi_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csi_pad_ops = {
++ .enum_mbus_code = csi_enum_mbus_code,
++ .enum_frame_size = csi_enum_frame_size,
++ .get_fmt = csi_get_format,
++ .set_fmt = csi_set_format,
++};
++
++static const struct v4l2_subdev_ops csi_v4l2_ops = {
++ .core = &csi_core_ops,
++ .video = &csi_video_ops,
++ .pad = &csi_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops csi_v4l2_internal_ops = {
++ .open = csi_init_formats,
++};
++
++static const struct media_entity_operations csi_media_ops = {
++ .link_setup = csi_link_setup,
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_csi_register(struct stf_csi_dev *csi_dev, struct v4l2_device *v4l2_dev)
++{
++ struct v4l2_subdev *sd = &csi_dev->subdev;
++ struct device *dev = csi_dev->stfcamss->dev;
++ struct media_pad *pads = csi_dev->pads;
++ int ret;
++
++ csi_dev->mipirx_1p8 = devm_regulator_get(dev, "mipirx_1p8");
++ if (IS_ERR(csi_dev->mipirx_1p8))
++ return PTR_ERR(csi_dev->mipirx_1p8);
++
++ csi_dev->mipirx_0p9 = devm_regulator_get(dev, "mipirx_0p9");
++ if (IS_ERR(csi_dev->mipirx_0p9))
++ return PTR_ERR(csi_dev->mipirx_0p9);
++
++ v4l2_subdev_init(sd, &csi_v4l2_ops);
++ sd->internal_ops = &csi_v4l2_internal_ops;
++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++ STF_CSI_NAME, 0);
++ v4l2_set_subdevdata(sd, csi_dev);
++
++ ret = csi_init_formats(sd, NULL);
++ if (ret < 0) {
++ dev_err(dev, "Failed to init format: %d\n", ret);
++ return ret;
++ }
++
++ pads[STF_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++ pads[STF_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->entity.ops = &csi_media_ops;
++ ret = media_entity_pads_init(&sd->entity, STF_CSI_PADS_NUM, pads);
++ if (ret < 0) {
++ dev_err(dev, "Failed to init media entity: %d\n", ret);
++ return ret;
++ }
++
++ ret = v4l2_device_register_subdev(v4l2_dev, sd);
++ if (ret < 0) {
++ dev_err(dev, "Failed to register subdev: %d\n", ret);
++ goto err_sreg;
++ }
++
++ return 0;
++
++err_sreg:
++ media_entity_cleanup(&sd->entity);
++ return ret;
++}
++
++int stf_csi_unregister(struct stf_csi_dev *csi_dev)
++{
++ v4l2_device_unregister_subdev(&csi_dev->subdev);
++ media_entity_cleanup(&csi_dev->subdev.entity);
++ mutex_destroy(&csi_dev->stream_lock);
++ return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.h
+@@ -0,0 +1,61 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_CSI_H
++#define STF_CSI_H
++
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_CSI_NAME "stf_csi"
++
++#define STF_CSI_PAD_SINK 0
++#define STF_CSI_PAD_SRC 1
++#define STF_CSI_PADS_NUM 2
++
++struct csi_format {
++ u32 code;
++ u8 bpp;
++};
++
++struct stf_csi_dev;
++
++struct csi_hw_ops {
++ int (*csi_power_on)(struct stf_csi_dev *csi_dev, u8 on);
++ int (*csi_clk_enable)(struct stf_csi_dev *csi_dev);
++ int (*csi_clk_disable)(struct stf_csi_dev *csi_dev);
++ int (*csi_stream_set)(struct stf_csi_dev *csi_dev, int on,
++ u32 dt, u32 width, u8 bpp);
++};
++
++struct stf_csi_dev {
++ struct stfcamss *stfcamss;
++ enum sensor_type s_type;
++ struct v4l2_subdev subdev;
++ struct media_pad pads[STF_CSI_PADS_NUM];
++ struct v4l2_mbus_framefmt fmt[STF_CSI_PADS_NUM];
++ const struct csi_format *formats_sink;
++ unsigned int nformats_sink;
++ const struct csi_format *formats_src;
++ unsigned int nformats_src;
++ struct csi_hw_ops *hw_ops;
++ struct mutex stream_lock;
++ int stream_count;
++ struct regulator *mipirx_1p8;
++ struct regulator *mipirx_0p9;
++};
++
++extern int stf_csi_subdev_init(struct stfcamss *stfcamss);
++extern int stf_csi_register(struct stf_csi_dev *csi_dev,
++ struct v4l2_device *v4l2_dev);
++extern int stf_csi_unregister(struct stf_csi_dev *csi_dev);
++extern struct csi_hw_ops csi_ops;
++extern void dump_csi_reg(void *__iomem csibase);
++
++#endif /* STF_CSI_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
+@@ -0,0 +1,310 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/regmap.h>
++
++#define CSI2RX_DEVICE_CFG_REG 0x000
++
++#define CSI2RX_SOFT_RESET_REG 0x004
++#define CSI2RX_SOFT_RESET_PROTOCOL BIT(1)
++#define CSI2RX_SOFT_RESET_FRONT BIT(0)
++
++#define CSI2RX_DPHY_LANE_CONTROL 0x040
++
++#define CSI2RX_STATIC_CFG_REG 0x008
++#define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) \
++ ((plane) << (16 + (llane) * 4))
++#define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8)
++
++#define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100)
++
++#define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000)
++#define CSI2RX_STREAM_CTRL_START BIT(0)
++
++#define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008)
++#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31)
++#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 BIT(7)
++#define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16)
++
++#define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c)
++#define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8)
++
++#define CSI2RX_LANES_MAX 4
++#define CSI2RX_STREAMS_MAX 4
++
++static int stf_csi_power_on(struct stf_csi_dev *csi_dev, u8 on)
++{
++ struct stfcamss *stfcamss = csi_dev->stfcamss;
++ int ret;
++
++ if (on) {
++ ret = regulator_enable(csi_dev->mipirx_1p8);
++ if (ret) {
++ st_err(ST_CSI, "Cannot enable mipirx_1p8 regulator\n");
++ goto err_1p8;
++ }
++
++ ret = regulator_enable(csi_dev->mipirx_0p9);
++ if (ret) {
++ st_err(ST_CSI, "Cannot enable mipirx_0p9 regulator\n");
++ goto err_0p9;
++ }
++ } else {
++ regulator_disable(csi_dev->mipirx_1p8);
++ regulator_disable(csi_dev->mipirx_0p9);
++ }
++
++ regmap_update_bits(stfcamss->stf_aon_syscon, stfcamss->aon_gp_reg,
++ BIT(31), BIT(31));
++
++ return 0;
++
++err_0p9:
++ regulator_disable(csi_dev->mipirx_1p8);
++err_1p8:
++ return ret;
++
++}
++
++static int stf_csi_clk_enable(struct stf_csi_dev *csi_dev)
++{
++ struct stfcamss *stfcamss = csi_dev->stfcamss;
++
++ clk_set_rate(stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk, 198000000);
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
++
++ reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
++
++ switch (csi_dev->s_type) {
++ case SENSOR_VIN:
++ reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++ clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++ stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++ break;
++ case SENSOR_ISP:
++ clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++ stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++ break;
++ }
++
++ return 0;
++}
++
++static int stf_csi_clk_disable(struct stf_csi_dev *csi_dev)
++{
++ struct stfcamss *stfcamss = csi_dev->stfcamss;
++
++ switch (csi_dev->s_type) {
++ case SENSOR_VIN:
++ reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++ break;
++ case SENSOR_ISP:
++ break;
++ }
++
++ reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
++
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
++
++ return 0;
++}
++
++static void csi2rx_reset(void *reg_base)
++{
++ writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
++ reg_base + CSI2RX_SOFT_RESET_REG);
++
++ udelay(10);
++
++ writel(0, reg_base + CSI2RX_SOFT_RESET_REG);
++}
++
++static int csi2rx_start(struct stf_csi_dev *csi_dev, void *reg_base, u32 dt)
++{
++ struct stfcamss *stfcamss = csi_dev->stfcamss;
++ struct csi2phy_cfg *csiphy =
++ stfcamss->csiphy_dev->csiphy;
++ unsigned int i;
++ unsigned long lanes_used = 0;
++ u32 reg;
++
++ if (!csiphy) {
++ st_err(ST_CSI, "csiphy0 config not exist\n");
++ return -EINVAL;
++ }
++
++ csi2rx_reset(reg_base);
++
++ reg = csiphy->num_data_lanes << 8;
++ for (i = 0; i < csiphy->num_data_lanes; i++) {
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++ reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csiphy->data_lanes[i]);
++ set_bit(csiphy->data_lanes[i] - 1, &lanes_used);
++#else
++ reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
++ set_bit(i, &lanes_used);
++#endif
++ }
++
++ /*
++ * Even the unused lanes need to be mapped. In order to avoid
++ * to map twice to the same physical lane, keep the lanes used
++ * in the previous loop, and only map unused physical lanes to
++ * the rest of our logical lanes.
++ */
++ for (i = csiphy->num_data_lanes; i < CSI2RX_LANES_MAX; i++) {
++ unsigned int idx = find_first_zero_bit(&lanes_used,
++ CSI2RX_LANES_MAX);
++
++ set_bit(idx, &lanes_used);
++ reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, idx + 1);
++ }
++
++ writel(reg, reg_base + CSI2RX_STATIC_CFG_REG);
++
++ // 0x40 DPHY_LANE_CONTROL
++ reg = 0;
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++ for (i = 0; i < csiphy->num_data_lanes; i++)
++ reg |= 1 << (csiphy->data_lanes[i] - 1)
++ | 1 << (csiphy->data_lanes[i] + 11);
++#else
++ for (i = 0; i < csiphy->num_data_lanes; i++)
++ reg |= 1 << i | 1 << (i + 12); //data_clane
++#endif
++
++ reg |= 1 << 4 | 1 << 16; //clk_lane
++ writel(reg, reg_base + CSI2RX_DPHY_LANE_CONTROL);
++
++ /*
++ * Create a static mapping between the CSI virtual channels
++ * and the output stream.
++ *
++ * This should be enhanced, but v4l2 lacks the support for
++ * changing that mapping dynamically.
++ *
++ * We also cannot enable and disable independent streams here,
++ * hence the reference counting.
++ */
++ for (i = 0; i < CSI2RX_STREAMS_MAX; i++) {
++ writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
++ reg_base + CSI2RX_STREAM_CFG_REG(i));
++
++ writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT |
++ CSI2RX_STREAM_DATA_CFG_VC_SELECT(i) |
++ CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt,
++ reg_base + CSI2RX_STREAM_DATA_CFG_REG(i));
++
++ writel(CSI2RX_STREAM_CTRL_START,
++ reg_base + CSI2RX_STREAM_CTRL_REG(i));
++ }
++
++ return 0;
++}
++
++static void csi2rx_stop(struct stf_csi_dev *csi_dev, void *reg_base)
++{
++ unsigned int i;
++
++ for (i = 0; i < CSI2RX_STREAMS_MAX; i++)
++ writel(0, reg_base + CSI2RX_STREAM_CTRL_REG(i));
++}
++
++static void csi_set_vin_axiwr_pix(struct stf_csi_dev *csi_dev, u32 width, u8 bpp)
++{
++ struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
++ u32 value = 0;
++ int cnfg_axiwr_pix_ct = 64 / bpp;
++
++ if (cnfg_axiwr_pix_ct == 2)
++ value = 0;
++ else if (cnfg_axiwr_pix_ct == 4)
++ value = 1;
++ else if (cnfg_axiwr_pix_ct == 8)
++ value = 2;
++
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ BIT(14)|BIT(13), value << 13); //u0_vin_cnfg_axiwr0_pix_ct
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2),
++ (width / cnfg_axiwr_pix_ct - 1)<<2); //u0_vin_cnfg_axiwr0_pix_cnt_end
++}
++
++static int stf_csi_stream_set(struct stf_csi_dev *csi_dev,
++ int on, u32 dt, u32 width, u8 bpp)
++{
++ struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
++ void __iomem *reg_base = vin->csi2rx_base;
++
++ switch (csi_dev->s_type) {
++ case SENSOR_VIN:
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20,
++ BIT(3)|BIT(2)|BIT(1)|BIT(0),
++ 0<<0); //u0_vin_cnfg_axiwr0_channel_sel
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ BIT(16)|BIT(15),
++ 0<<15); //u0_vin_cnfg_axiwr0_pixel_high_bit_sel
++ csi_set_vin_axiwr_pix(csi_dev, width, bpp);
++ break;
++ case SENSOR_ISP:
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ BIT(7)|BIT(6),
++ 0<<6); //u0_vin_cnfg_mipi_byte_en_isp
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ BIT(11)|BIT(10)|BIT(9)|BIT(8),
++ 0<<8); //u0_vin_cnfg_mipi_channel_sel0
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ BIT(16)|BIT(15)|BIT(14)|BIT(13),
++ 0<<13); //u0_vin_cnfg_pix_num
++
++ if (dt == 0x2b)
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ BIT(12),
++ 1<<12); //u0_vin_cnfg_p_i_mipi_header_en0
++ break;
++ }
++
++ if (on)
++ csi2rx_start(csi_dev, reg_base, dt);
++ else
++ csi2rx_stop(csi_dev, reg_base);
++
++ return 0;
++}
++
++void dump_csi_reg(void *__iomem csibase)
++{
++ st_info(ST_CSI, "DUMP CSI register:\n");
++ print_reg(ST_CSI, csibase, 0x00);
++ print_reg(ST_CSI, csibase, 0x04);
++ print_reg(ST_CSI, csibase, 0x08);
++ print_reg(ST_CSI, csibase, 0x10);
++
++ print_reg(ST_CSI, csibase, 0x40);
++ print_reg(ST_CSI, csibase, 0x48);
++ print_reg(ST_CSI, csibase, 0x4c);
++ print_reg(ST_CSI, csibase, 0x50);
++}
++
++struct csi_hw_ops csi_ops = {
++ .csi_power_on = stf_csi_power_on,
++ .csi_clk_enable = stf_csi_clk_enable,
++ .csi_clk_disable = stf_csi_clk_disable,
++ .csi_stream_set = stf_csi_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
+@@ -0,0 +1,357 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct csiphy_format csiphy_formats_st7110[] = {
++ { MEDIA_BUS_FMT_UYVY8_2X8, 16},
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++int stf_csiphy_subdev_init(struct stfcamss *stfcamss)
++{
++ struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++
++ csiphy_dev->hw_ops = &csiphy_ops;
++ csiphy_dev->stfcamss = stfcamss;
++ csiphy_dev->formats = csiphy_formats_st7110;
++ csiphy_dev->nformats = ARRAY_SIZE(csiphy_formats_st7110);
++ mutex_init(&csiphy_dev->stream_lock);
++ return 0;
++}
++
++static int csiphy_set_power(struct v4l2_subdev *sd, int on)
++{
++ return 0;
++}
++
++static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++
++ mutex_lock(&csiphy_dev->stream_lock);
++ if (enable) {
++ if (csiphy_dev->stream_count == 0) {
++ csiphy_dev->hw_ops->csiphy_clk_enable(csiphy_dev);
++ csiphy_dev->hw_ops->csiphy_config_set(csiphy_dev);
++ csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 1);
++ }
++ csiphy_dev->stream_count++;
++ } else {
++ if (csiphy_dev->stream_count == 0)
++ goto exit;
++ if (csiphy_dev->stream_count == 1) {
++ csiphy_dev->hw_ops->csiphy_clk_disable(csiphy_dev);
++ csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 0);
++ }
++ csiphy_dev->stream_count--;
++ }
++exit:
++ mutex_unlock(&csiphy_dev->stream_lock);
++
++ return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__csiphy_get_format(struct stf_csiphy_dev *csiphy_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_format(
++ &csiphy_dev->subdev,
++ state,
++ pad);
++
++ return &csiphy_dev->fmt[pad];
++}
++
++static void csiphy_try_format(struct stf_csiphy_dev *csiphy_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ struct v4l2_mbus_framefmt *fmt,
++ enum v4l2_subdev_format_whence which)
++{
++ unsigned int i;
++
++ switch (pad) {
++ case STF_CSIPHY_PAD_SINK:
++ /* Set format on sink pad */
++
++ for (i = 0; i < csiphy_dev->nformats; i++)
++ if (fmt->code == csiphy_dev->formats[i].code)
++ break;
++
++ if (i >= csiphy_dev->nformats)
++ fmt->code = csiphy_dev->formats[0].code;
++
++ fmt->width = clamp_t(u32,
++ fmt->width,
++ STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height,
++ STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++
++ break;
++
++ case STF_CSIPHY_PAD_SRC:
++
++ *fmt = *__csiphy_get_format(csiphy_dev,
++ state,
++ STF_CSIPHY_PAD_SINK, which);
++
++ break;
++ }
++}
++
++static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++
++ if (code->index >= csiphy_dev->nformats)
++ return -EINVAL;
++
++ if (code->pad == STF_CSIPHY_PAD_SINK) {
++ code->code = csiphy_dev->formats[code->index].code;
++ } else {
++ struct v4l2_mbus_framefmt *sink_fmt;
++
++ sink_fmt = __csiphy_get_format(csiphy_dev, state,
++ STF_CSIPHY_PAD_SINK,
++ code->which);
++
++ code->code = sink_fmt->code;
++ if (!code->code)
++ return -EINVAL;
++ }
++ code->flags = 0;
++ return 0;
++}
++
++static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt format;
++
++ if (fse->index != 0)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = 1;
++ format.height = 1;
++ csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
++ fse->min_width = format.width;
++ fse->min_height = format.height;
++
++ if (format.code != fse->code)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = -1;
++ format.height = -1;
++ csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
++ fse->max_width = format.width;
++ fse->max_height = format.height;
++
++ return 0;
++}
++
++static int csiphy_get_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ fmt->format = *format;
++
++ return 0;
++}
++
++static int csiphy_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ mutex_lock(&csiphy_dev->stream_lock);
++ if (csiphy_dev->stream_count) {
++ fmt->format = *format;
++ mutex_unlock(&csiphy_dev->stream_lock);
++ goto out;
++ } else {
++ csiphy_try_format(csiphy_dev, state, fmt->pad, &fmt->format, fmt->which);
++ *format = fmt->format;
++ }
++ mutex_unlock(&csiphy_dev->stream_lock);
++
++ /* Propagate the format from sink to source */
++ if (fmt->pad == STF_CSIPHY_PAD_SINK) {
++ format = __csiphy_get_format(csiphy_dev,
++ state,
++ STF_CSIPHY_PAD_SRC,
++ fmt->which);
++
++ *format = fmt->format;
++ csiphy_try_format(csiphy_dev, state, STF_CSIPHY_PAD_SRC, format,
++ fmt->which);
++ }
++out:
++ return 0;
++}
++
++static int csiphy_init_formats(struct v4l2_subdev *sd,
++ struct v4l2_subdev_fh *fh)
++{
++ struct v4l2_subdev_format format = {
++ .pad = STF_CSIPHY_PAD_SINK,
++ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++ V4L2_SUBDEV_FORMAT_ACTIVE,
++ .format = {
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .width = 1920,
++ .height = 1080
++ }
++ };
++
++ return csiphy_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int csiphy_link_setup(struct media_entity *entity,
++ const struct media_pad *local,
++ const struct media_pad *remote, u32 flags)
++{
++ if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++ (flags & MEDIA_LNK_FL_ENABLED)) {
++ struct v4l2_subdev *sd;
++ struct stf_csiphy_dev *csiphy_dev;
++ struct stf_csi_dev *csi_dev;
++
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++
++ sd = media_entity_to_v4l2_subdev(entity);
++ csiphy_dev = v4l2_get_subdevdata(sd);
++
++ sd = media_entity_to_v4l2_subdev(remote->entity);
++ csi_dev = v4l2_get_subdevdata(sd);
++ st_info(ST_CSIPHY, "CSIPHY0 link to CSI0\n");
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops csiphy_core_ops = {
++ .s_power = csiphy_set_power,
++};
++
++static const struct v4l2_subdev_video_ops csiphy_video_ops = {
++ .s_stream = csiphy_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
++ .enum_mbus_code = csiphy_enum_mbus_code,
++ .enum_frame_size = csiphy_enum_frame_size,
++ .get_fmt = csiphy_get_format,
++ .set_fmt = csiphy_set_format,
++};
++
++static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
++ .core = &csiphy_core_ops,
++ .video = &csiphy_video_ops,
++ .pad = &csiphy_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
++ .open = csiphy_init_formats,
++};
++
++static const struct media_entity_operations csiphy_media_ops = {
++ .link_setup = csiphy_link_setup,
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
++ struct v4l2_device *v4l2_dev)
++{
++ struct v4l2_subdev *sd = &csiphy_dev->subdev;
++ struct device *dev = csiphy_dev->stfcamss->dev;
++ struct media_pad *pads = csiphy_dev->pads;
++ int ret;
++
++ v4l2_subdev_init(sd, &csiphy_v4l2_ops);
++ sd->internal_ops = &csiphy_v4l2_internal_ops;
++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++ STF_CSIPHY_NAME, 0);
++ v4l2_set_subdevdata(sd, csiphy_dev);
++
++ ret = csiphy_init_formats(sd, NULL);
++ if (ret < 0) {
++ dev_err(dev, "Failed to init format: %d\n", ret);
++ return ret;
++ }
++
++ pads[STF_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++ pads[STF_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->entity.ops = &csiphy_media_ops;
++ ret = media_entity_pads_init(&sd->entity, STF_CSIPHY_PADS_NUM, pads);
++ if (ret < 0) {
++ dev_err(dev, "Failed to init media entity: %d\n", ret);
++ return ret;
++ }
++
++ ret = v4l2_device_register_subdev(v4l2_dev, sd);
++ if (ret < 0) {
++ dev_err(dev, "Failed to register subdev: %d\n", ret);
++ goto err_sreg;
++ }
++
++ return 0;
++
++err_sreg:
++ media_entity_cleanup(&sd->entity);
++ return ret;
++}
++
++int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev)
++{
++ v4l2_device_unregister_subdev(&csiphy_dev->subdev);
++ media_entity_cleanup(&csiphy_dev->subdev.entity);
++ mutex_destroy(&csiphy_dev->stream_lock);
++ return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
+@@ -0,0 +1,188 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_CSIPHY_H
++#define STF_CSIPHY_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_CSIPHY_NAME "stf_csiphy"
++
++#define STF_CSIPHY_PAD_SINK 0
++#define STF_CSIPHY_PAD_SRC 1
++#define STF_CSIPHY_PADS_NUM 2
++
++#define STF_CSI2_MAX_DATA_LANES 4
++
++union static_config {
++ u32 raw;
++ struct {
++ u32 sel : 2;
++ u32 rsvd_6 : 2;
++ u32 v2p0_support_enable : 1;
++ u32 rsvd_5 : 3;
++ u32 lane_nb : 3;
++ u32 rsvd_4 : 5;
++ u32 dl0_map : 3;
++ u32 rsvd_3 : 1;
++ u32 dl1_map : 3;
++ u32 rsvd_2 : 1;
++ u32 dl2_map : 3;
++ u32 rsvd_1 : 1;
++ u32 dl3_map : 3;
++ u32 rsvd_0 : 1;
++ } bits;
++};
++
++union error_bypass_cfg {
++ u32 value;
++ struct {
++ u32 crc : 1;
++ u32 ecc : 1;
++ u32 data_id : 1;
++ u32 rsvd_0 : 29;
++ };
++};
++
++union stream_monitor_ctrl {
++ u32 value;
++ struct {
++ u32 lb_vc : 4;
++ u32 lb_en : 1;
++ u32 timer_vc : 4;
++ u32 timer_en : 1;
++ u32 timer_eof : 1;
++ u32 frame_mon_vc : 4;
++ u32 frame_mon_en : 1;
++ u32 frame_length : 16;
++ };
++};
++
++union stream_cfg {
++ u32 value;
++ struct {
++ u32 interface_mode : 1;
++ u32 ls_le_mode : 1;
++ u32 rsvd_3 : 2;
++ u32 num_pixels : 2;
++ u32 rsvd_2 : 2;
++ u32 fifo_mode : 2;
++ u32 rsvd_1 : 2;
++ u32 bpp_bypass : 3;
++ u32 rsvd_0 : 1;
++ u32 fifo_fill : 16;
++ };
++};
++
++union dphy_lane_ctrl {
++ u32 raw;
++ struct {
++ u32 dl0_en : 1;
++ u32 dl1_en : 1;
++ u32 dl2_en : 1;
++ u32 dl3_en : 1;
++ u32 cl_en : 1;
++ u32 rsvd_1 : 7;
++ u32 dl0_reset : 1;
++ u32 dl1_reset : 1;
++ u32 dl2_reset : 1;
++ u32 dl3_reset : 1;
++ u32 cl_reset : 1;
++ u32 rsvd_0 : 15;
++ } bits;
++};
++
++union dphy_lane_swap {
++ u32 raw;
++ struct {
++ u32 rx_1c2c_sel : 1;
++ u32 lane_swap_clk : 3;
++ u32 lane_swap_clk1 : 3;
++ u32 lane_swap_lan0 : 3;
++ u32 lane_swap_lan1 : 3;
++ u32 lane_swap_lan2 : 3;
++ u32 lane_swap_lan3 : 3;
++ u32 dpdn_swap_clk : 1;
++ u32 dpdn_swap_clk1 : 1;
++ u32 dpdn_swap_lan0 : 1;
++ u32 dpdn_swap_lan1 : 1;
++ u32 dpdn_swap_lan2 : 1;
++ u32 dpdn_swap_lan3 : 1;
++ u32 hs_freq_chang_clk0 : 1;
++ u32 hs_freq_chang_clk1 : 1;
++ u32 reserved : 5;
++ } bits;
++};
++
++union dphy_lane_en {
++ u32 raw;
++ struct {
++ u32 gpio_en : 6;
++ u32 mp_test_mode_sel : 5;
++ u32 mp_test_en : 1;
++ u32 dphy_enable_lan0 : 1;
++ u32 dphy_enable_lan1 : 1;
++ u32 dphy_enable_lan2 : 1;
++ u32 dphy_enable_lan3 : 1;
++ u32 rsvd_0 : 16;
++ } bits;
++};
++
++struct csiphy_format {
++ u32 code;
++ u8 bpp;
++};
++
++struct csi2phy_cfg {
++ unsigned int flags;
++ unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
++ unsigned char clock_lane;
++ unsigned char num_data_lanes;
++ bool lane_polarities[1 + STF_CSI2_MAX_DATA_LANES];
++};
++
++struct csi2phy_cfg2 {
++ unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
++ unsigned char num_data_lanes;
++ unsigned char num_clks;
++ unsigned char clock_lane;
++ unsigned char clock1_lane;
++ bool lane_polarities[2 + STF_CSI2_MAX_DATA_LANES];
++};
++
++struct stf_csiphy_dev;
++
++struct csiphy_hw_ops {
++ int (*csiphy_clk_enable)(struct stf_csiphy_dev *csiphy_dev);
++ int (*csiphy_clk_disable)(struct stf_csiphy_dev *csiphy_dev);
++ int (*csiphy_config_set)(struct stf_csiphy_dev *csiphy_dev);
++ int (*csiphy_stream_set)(struct stf_csiphy_dev *csiphy_dev, int on);
++};
++
++struct stf_csiphy_dev {
++ struct stfcamss *stfcamss;
++ struct csi2phy_cfg *csiphy;
++ struct v4l2_subdev subdev;
++ struct media_pad pads[STF_CSIPHY_PADS_NUM];
++ struct v4l2_mbus_framefmt fmt[STF_CSIPHY_PADS_NUM];
++ const struct csiphy_format *formats;
++ unsigned int nformats;
++ struct csiphy_hw_ops *hw_ops;
++ struct mutex stream_lock;
++ int stream_count;
++};
++
++extern int stf_csiphy_subdev_init(struct stfcamss *stfcamss);
++extern int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
++ struct v4l2_device *v4l2_dev);
++extern int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev);
++
++extern struct csiphy_hw_ops csiphy_ops;
++
++#endif /* STF_CSIPHY_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
+@@ -0,0 +1,335 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/sort.h>
++
++static int stf_csiphy_clk_set(struct stf_csiphy_dev *csiphy_dev, int on)
++{
++ struct stfcamss *stfcamss = csiphy_dev->stfcamss;
++ static int init_flag;
++ static struct mutex count_lock;
++ static int count;
++
++ if (!init_flag) {
++ init_flag = 1;
++ mutex_init(&count_lock);
++ }
++ mutex_lock(&count_lock);
++ if (on) {
++ clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_CFGCLK_IN].clk,
++ 99000000);
++ clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_REFCLK_IN].clk,
++ 49500000);
++ clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_TXCLKESC_LAN0].clk,
++ 19800000);
++
++ reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
++
++ count++;
++ } else {
++ if (count == 0)
++ goto exit;
++ if (count == 1) {
++ reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
++ }
++ count--;
++ }
++exit:
++ mutex_unlock(&count_lock);
++ return 0;
++}
++
++static int stf_csiphy_clk_enable(struct stf_csiphy_dev *csiphy_dev)
++{
++ return stf_csiphy_clk_set(csiphy_dev, 1);
++}
++
++static int stf_csiphy_clk_disable(struct stf_csiphy_dev *csiphy_dev)
++{
++ return stf_csiphy_clk_set(csiphy_dev, 0);
++}
++
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++static int cmp_func(const void *x1, const void *x2)
++{
++ return *((unsigned char *)x1) - *((unsigned char *)x2);
++}
++#endif
++
++int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
++ struct csi2phy_cfg *cfg1)
++{
++ int i = 0;
++
++ cfg->clock_lane = 0;
++ cfg->clock1_lane = 5;
++ cfg->data_lanes[0] = 1;
++ cfg->data_lanes[1] = 2;
++ cfg->data_lanes[2] = 3;
++ cfg->data_lanes[3] = 4;
++
++ if (cfg0 && cfg1) {
++ st_debug(ST_CSIPHY, "CSIPHY use 2 clk mode\n");
++ cfg->num_clks = 2;
++ cfg->num_data_lanes =
++ cfg1->num_data_lanes + cfg0->num_data_lanes;
++ if (cfg->num_data_lanes > STF_CSI2_MAX_DATA_LANES)
++ return -EINVAL;
++ cfg->clock_lane = cfg0->clock_lane;
++ cfg->lane_polarities[0] = cfg0->lane_polarities[0];
++ cfg->clock1_lane = cfg1->clock_lane;
++ cfg->lane_polarities[1] = cfg1->lane_polarities[0];
++ for (i = 0; i < cfg0->num_data_lanes; i++) {
++ cfg->data_lanes[i] = cfg0->data_lanes[i];
++ cfg->lane_polarities[i + 2] =
++ cfg0->lane_polarities[i + 1];
++ }
++
++ for (i = cfg0->num_data_lanes; i < cfg->num_data_lanes; i++) {
++ cfg->data_lanes[i] =
++ cfg1->data_lanes[i - cfg0->num_data_lanes];
++ cfg->lane_polarities[i + 2] =
++ cfg1->lane_polarities[i - cfg0->num_data_lanes + 1];
++ }
++ } else if (cfg0 && !cfg1) {
++ st_debug(ST_CSIPHY, "CSIPHY cfg0 use 1 clk mode\n");
++ cfg->num_clks = 1;
++ cfg->num_data_lanes = cfg0->num_data_lanes;
++ cfg->clock_lane = cfg->clock1_lane = cfg0->clock_lane;
++ cfg->lane_polarities[0] = cfg->lane_polarities[1] =
++ cfg0->lane_polarities[0];
++ for (i = 0; i < cfg0->num_data_lanes; i++) {
++ cfg->data_lanes[i] = cfg0->data_lanes[i];
++ cfg->lane_polarities[i + 2] = cfg0->lane_polarities[i + 1];
++ }
++ } else if (!cfg0 && cfg1) {
++ st_debug(ST_CSIPHY, "CSIPHY cfg1 use 1 clk mode\n");
++ cfg->num_clks = 1;
++ cfg->num_data_lanes = cfg1->num_data_lanes;
++ cfg->clock_lane = cfg->clock1_lane = cfg1->clock_lane;
++ cfg->lane_polarities[0] = cfg->lane_polarities[1] =
++ cfg1->lane_polarities[0];
++ for (i = 0; i < cfg1->num_data_lanes; i++) {
++ cfg->data_lanes[i] = cfg1->data_lanes[i];
++ cfg->lane_polarities[i + 2] = cfg1->lane_polarities[i + 1];
++ }
++ } else {
++ return -EINVAL;
++ }
++
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++ sort(cfg->data_lanes, cfg->num_data_lanes,
++ sizeof(cfg->data_lanes[0]), cmp_func, NULL);
++#endif
++ for (i = 0; i < cfg->num_data_lanes; i++)
++ st_debug(ST_CSIPHY, "%d: %d\n", i, cfg->data_lanes[i]);
++ return 0;
++}
++
++static int csi2rx_dphy_config(struct stf_vin_dev *vin,
++ struct stf_csiphy_dev *csiphy_dev)
++{
++ struct csi2phy_cfg2 cfg2 = {0};
++ struct csi2phy_cfg2 *cfg = &cfg2;
++ struct csi2phy_cfg *phycfg = csiphy_dev->csiphy;
++
++ if (!phycfg)
++ return -EINVAL;
++
++ if (try_cfg(cfg, phycfg, NULL))
++ return -EINVAL;
++
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_4, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_8, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_12, 0xfff0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_16, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_20, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_24, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_28, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_32, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_36, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_40, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_44, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_48, 0x24000000);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_52, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_56, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_60, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_64, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_68, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_72, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_76, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_80, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_84, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_88, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_92, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_96, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_100, 0x02000000);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_104, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_108, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_112, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_116, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_120, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_124, 0xc);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_128, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_132, 0xcc500000);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_136, 0xcc);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_140, 0x0);
++ reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_144, 0x0);
++
++ reg_set_bit(vin->rstgen_base, //r100_ctrl0_2d1c_efuse_en
++ M31DPHY_APBCFGSAIF__SYSCFG_0,
++ BIT(6), 1<<6);
++ reg_set_bit(vin->rstgen_base, //r100_ctrl0_2d1c_efuse_in
++ M31DPHY_APBCFGSAIF__SYSCFG_0,
++ BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0x1b<<7);
++ reg_set_bit(vin->rstgen_base, //r100_ctrl1_2d1c_efuse_en
++ M31DPHY_APBCFGSAIF__SYSCFG_0,
++ BIT(13), 1<<13);
++ reg_set_bit(vin->rstgen_base, //r100_ctrl1_2d1c_efuse_in
++ M31DPHY_APBCFGSAIF__SYSCFG_0,
++ BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14), 0x1b<<14);
++
++ reg_set_bit(vin->rstgen_base, //data_bus16_8
++ M31DPHY_APBCFGSAIF__SYSCFG_184,
++ BIT(8), 0<<8);
++
++ reg_set_bit(vin->rstgen_base, //debug_mode_sel
++ M31DPHY_APBCFGSAIF__SYSCFG_184,
++ BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9), 0x5a<<9);
++
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_clk0
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(0), cfg->lane_polarities[0]<<0);
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_clk1
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(1), cfg->lane_polarities[1]<<1);
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_lan0
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(2), cfg->lane_polarities[2]<<2);
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_lan1
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(3), cfg->lane_polarities[3]<<3);
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_lan2
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(4), cfg->lane_polarities[4]<<4);
++ reg_set_bit(vin->rstgen_base, //dpdn_swap_lan3
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(5), cfg->lane_polarities[5]<<5);
++ reg_set_bit(vin->rstgen_base, //enable clk0
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(6), 1<<6);
++ reg_set_bit(vin->rstgen_base, //enable clk1
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(7), 1<<7);
++ reg_set_bit(vin->rstgen_base, //enable lan0
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(8), 1<<8);
++ reg_set_bit(vin->rstgen_base, //enable lan1
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(9), 1<<9);
++ reg_set_bit(vin->rstgen_base, //enable lan2
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(10), 1<<10);
++ reg_set_bit(vin->rstgen_base, //enable lan3
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(11), 1<<11);
++ reg_set_bit(vin->rstgen_base, //gpi_en
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
++ 0<<12);
++ reg_set_bit(vin->rstgen_base, //hs_freq_change_clk0
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(18), 0<<18);
++ reg_set_bit(vin->rstgen_base, //hs_freq_change_clk1
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(19), 0<<19);
++
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(22)|BIT(21)|BIT(20), cfg->clock_lane<<20); //clock lane 0
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(25)|BIT(24)|BIT(23), cfg->clock1_lane<<23); //clock lane 1
++
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(28)|BIT(27)|BIT(26), cfg->data_lanes[0]<<26); //data lane 0
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_188,
++ BIT(31)|BIT(30)|BIT(29), cfg->data_lanes[1]<<29); //data lane 1
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(2)|BIT(1)|BIT(0), cfg->data_lanes[2]<<0); //data lane 2
++ reg_set_bit(vin->rstgen_base,
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(5)|BIT(4)|BIT(3), cfg->data_lanes[3]<<3); //data lane 3
++
++ reg_set_bit(vin->rstgen_base, //mp_test_en
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(6), 0<<6);
++ reg_set_bit(vin->rstgen_base, //mp_test_mode_sel
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0<<7);
++
++ reg_set_bit(vin->rstgen_base, //pll_clk_sel
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
++ 0x37c<<12);
++
++ reg_set_bit(vin->rstgen_base, //rx_1c2c_sel
++ M31DPHY_APBCFGSAIF__SYSCFG_200,
++ BIT(8), 0<<8);
++
++ reg_set_bit(vin->rstgen_base, //precounter in clk0
++ M31DPHY_APBCFGSAIF__SYSCFG_192,
++ BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24)|BIT(23)|BIT(22),
++ 8<<22);
++ reg_set_bit(vin->rstgen_base, //precounter in clk1
++ M31DPHY_APBCFGSAIF__SYSCFG_196,
++ BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
++ 8<<0);
++ reg_set_bit(vin->rstgen_base, //precounter in lan0
++ M31DPHY_APBCFGSAIF__SYSCFG_196,
++ BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8),
++ 7<<8);
++ reg_set_bit(vin->rstgen_base, //precounter in lan1
++ M31DPHY_APBCFGSAIF__SYSCFG_196,
++ BIT(23)|BIT(22)|BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16),
++ 7<<16);
++ reg_set_bit(vin->rstgen_base, //precounter in lan2
++ M31DPHY_APBCFGSAIF__SYSCFG_196,
++ BIT(31)|BIT(30)|BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24),
++ 7<<24);
++ reg_set_bit(vin->rstgen_base, //precounter in lan3
++ M31DPHY_APBCFGSAIF__SYSCFG_200,
++ BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
++ 7<<0);
++
++ return 0;
++}
++
++static int stf_csiphy_config_set(struct stf_csiphy_dev *csiphy_dev)
++{
++ struct stf_vin_dev *vin = csiphy_dev->stfcamss->vin;
++
++ csi2rx_dphy_config(vin, csiphy_dev);
++ return 0;
++}
++
++static int stf_csiphy_stream_set(struct stf_csiphy_dev *csiphy_dev, int on)
++{
++ return 0;
++}
++
++struct csiphy_hw_ops csiphy_ops = {
++ .csiphy_clk_enable = stf_csiphy_clk_enable,
++ .csiphy_clk_disable = stf_csiphy_clk_disable,
++ .csiphy_config_set = stf_csiphy_config_set,
++ .csiphy_stream_set = stf_csiphy_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
+@@ -0,0 +1,123 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/dma-buf.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "stf_isp_ioctl.h"
++#include "stf_dmabuf.h"
++
++#define TOTAL_SIZE_LIMIT (64 * 1024 * 1024)
++
++static size_t total_size;
++static struct vb2_queue vb2_queue = {
++ .dma_attrs = 0,
++ .gfp_flags = 0,
++ .dma_dir = DMA_TO_DEVICE,
++};
++static struct vb2_buffer vb = {
++ .vb2_queue = &vb2_queue,
++};
++
++static int dmabuf_create(struct device *dev,
++ struct dmabuf_create *head)
++{
++ struct dma_buf *dmabuf = NULL;
++ void *mem_priv = NULL;
++ dma_addr_t *paddr = NULL;
++ int ret = 0;
++
++ mem_priv = vb2_dma_contig_memops.alloc(&vb, dev, head->size);
++ if (IS_ERR_OR_NULL(mem_priv)) {
++ if (mem_priv)
++ ret = PTR_ERR(mem_priv);
++ goto exit;
++ }
++
++ dmabuf = vb2_dma_contig_memops.get_dmabuf(&vb, mem_priv, O_RDWR);
++ if (IS_ERR(dmabuf)) {
++ ret = PTR_ERR(dmabuf);
++ goto free;
++ }
++
++ head->fd = dma_buf_fd(dmabuf, O_CLOEXEC);
++ if (head->fd < 0) {
++ dma_buf_put(dmabuf);
++ ret = head->fd;
++ goto free;
++ }
++
++ paddr = vb2_dma_contig_memops.cookie(&vb, mem_priv);
++ head->paddr = *paddr;
++ return 0;
++free:
++ vb2_dma_contig_memops.put(mem_priv);
++exit:
++ return ret;
++}
++
++int stf_dmabuf_ioctl_alloc(struct device *dev, void *arg)
++{
++ struct dmabuf_create *head = arg;
++ int ret = -EINVAL;
++
++ if (IS_ERR_OR_NULL(head))
++ return -EFAULT;
++
++ head->size = PAGE_ALIGN(head->size);
++ if (!head->size)
++ return -EINVAL;
++ if ((head->size + total_size) > TOTAL_SIZE_LIMIT)
++ return -ENOMEM;
++
++ ret = dmabuf_create(dev, head);
++ if (ret)
++ return -EFAULT;
++
++ total_size += head->size;
++ return ret;
++}
++
++int stf_dmabuf_ioctl_free(struct device *dev, void *arg)
++{
++ struct dmabuf_create *head = arg;
++ struct dma_buf *dmabuf = NULL;
++ int ret = 0;
++
++ if (IS_ERR_OR_NULL(head))
++ return -EFAULT;
++ if (head->size != PAGE_ALIGN(head->size))
++ return -EINVAL;
++ if (head->size > total_size)
++ return -EINVAL;
++
++ dmabuf = dma_buf_get(head->fd);
++ if (IS_ERR_OR_NULL(dmabuf))
++ return -EINVAL;
++
++ dma_buf_put(dmabuf);
++ vb2_dma_contig_memops.put(dmabuf->priv);
++ total_size -= head->size;
++ return ret;
++}
++
++int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg)
++{
++ int ret = -ENOIOCTLCMD;
++
++ switch (cmd) {
++ case VIDIOC_STF_DMABUF_ALLOC:
++ ret = stf_dmabuf_ioctl_alloc(dev, arg);
++ break;
++ case VIDIOC_STF_DMABUF_FREE:
++ ret = stf_dmabuf_ioctl_free(dev, arg);
++ break;
++ default:
++ break;
++ }
++ return ret;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_DMABUF_H
++#define STF_DMABUF_H
++
++extern int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg);
++
++#endif /* STF_DMABUF_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
+@@ -0,0 +1,385 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct dvp_format dvp_formats_st7110[] = {
++ { MEDIA_BUS_FMT_YUYV8_2X8, 8},
++ { MEDIA_BUS_FMT_RGB565_2X8_LE, 8},
++ { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++ { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++ { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++ { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 8},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 8},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 8},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 8},
++};
++
++static int dvp_find_format(u32 code,
++ const struct dvp_format *formats,
++ unsigned int nformats)
++{
++ int i;
++
++ for (i = 0; i < nformats; i++)
++ if (formats[i].code == code)
++ return i;
++ return -EINVAL;
++}
++
++int stf_dvp_subdev_init(struct stfcamss *stfcamss)
++{
++ struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++
++ dvp_dev->s_type = SENSOR_VIN;
++ dvp_dev->hw_ops = &dvp_ops;
++ dvp_dev->stfcamss = stfcamss;
++ dvp_dev->formats = dvp_formats_st7110;
++ dvp_dev->nformats = ARRAY_SIZE(dvp_formats_st7110);
++ mutex_init(&dvp_dev->stream_lock);
++ dvp_dev->stream_count = 0;
++ return 0;
++}
++
++static int dvp_set_power(struct v4l2_subdev *sd, int on)
++{
++ return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__dvp_get_format(struct stf_dvp_dev *dvp_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which)
++{
++
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_format(
++ &dvp_dev->subdev, state, pad);
++ return &dvp_dev->fmt[pad];
++}
++
++static int dvp_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++ int ret = 0;
++
++ format = __dvp_get_format(dvp_dev, NULL, STF_DVP_PAD_SRC,
++ V4L2_SUBDEV_FORMAT_ACTIVE);
++ if (format == NULL)
++ return -EINVAL;
++ ret = dvp_find_format(format->code,
++ dvp_dev->formats,
++ dvp_dev->nformats);
++ if (ret < 0)
++ return ret;
++
++ mutex_lock(&dvp_dev->stream_lock);
++ if (enable) {
++ if (dvp_dev->stream_count == 0) {
++ dvp_dev->hw_ops->dvp_clk_enable(dvp_dev);
++ dvp_dev->hw_ops->dvp_config_set(dvp_dev);
++ dvp_dev->hw_ops->dvp_set_format(dvp_dev,
++ format->width, dvp_dev->formats[ret].bpp);
++ dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 1);
++ }
++ dvp_dev->stream_count++;
++ } else {
++ if (dvp_dev->stream_count == 0)
++ goto exit;
++ if (dvp_dev->stream_count == 1) {
++ dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 0);
++ dvp_dev->hw_ops->dvp_clk_disable(dvp_dev);
++ }
++ dvp_dev->stream_count--;
++ }
++exit:
++ mutex_unlock(&dvp_dev->stream_lock);
++ return 0;
++}
++
++static void dvp_try_format(struct stf_dvp_dev *dvp_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ struct v4l2_mbus_framefmt *fmt,
++ enum v4l2_subdev_format_whence which)
++{
++ unsigned int i;
++
++ switch (pad) {
++ case STF_DVP_PAD_SINK:
++ /* Set format on sink pad */
++
++ for (i = 0; i < dvp_dev->nformats; i++)
++ if (fmt->code == dvp_dev->formats[i].code)
++ break;
++
++ if (i >= dvp_dev->nformats)
++ fmt->code = dvp_dev->formats[0].code;
++
++ fmt->width = clamp_t(u32,
++ fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++
++ break;
++
++ case STF_DVP_PAD_SRC:
++
++ *fmt = *__dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK, which);
++
++ break;
++ }
++}
++
++static int dvp_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++
++ if (code->index >= dvp_dev->nformats)
++ return -EINVAL;
++
++ if (code->pad == STF_DVP_PAD_SINK) {
++ code->code = dvp_dev->formats[code->index].code;
++ } else {
++ struct v4l2_mbus_framefmt *sink_fmt;
++
++ sink_fmt = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK,
++ code->which);
++
++ code->code = sink_fmt->code;
++ if (!code->code)
++ return -EINVAL;
++ }
++ code->flags = 0;
++
++ return 0;
++}
++
++static int dvp_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt format;
++
++ if (fse->index != 0)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = 1;
++ format.height = 1;
++ dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
++ fse->min_width = format.width;
++ fse->min_height = format.height;
++
++ if (format.code != fse->code)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = -1;
++ format.height = -1;
++ dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
++ fse->max_width = format.width;
++ fse->max_height = format.height;
++
++ return 0;
++}
++
++static int dvp_get_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ fmt->format = *format;
++
++ return 0;
++}
++
++static int dvp_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ mutex_lock(&dvp_dev->stream_lock);
++ if (dvp_dev->stream_count) {
++ fmt->format = *format;
++ mutex_unlock(&dvp_dev->stream_lock);
++ goto out;
++ } else {
++ dvp_try_format(dvp_dev, state, fmt->pad, &fmt->format, fmt->which);
++ *format = fmt->format;
++ }
++ mutex_unlock(&dvp_dev->stream_lock);
++
++ /* Propagate the format from sink to source */
++ if (fmt->pad == STF_DVP_PAD_SINK) {
++ format = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SRC,
++ fmt->which);
++
++ *format = fmt->format;
++ dvp_try_format(dvp_dev, state, STF_DVP_PAD_SRC, format,
++ fmt->which);
++ }
++
++out:
++ return 0;
++}
++
++static int dvp_init_formats(struct v4l2_subdev *sd,
++ struct v4l2_subdev_fh *fh)
++{
++ struct v4l2_subdev_format format = {
++ .pad = STF_DVP_PAD_SINK,
++ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++ V4L2_SUBDEV_FORMAT_ACTIVE,
++ .format = {
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .width = 1920,
++ .height = 1080
++ }
++ };
++
++ return dvp_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int dvp_link_setup(struct media_entity *entity,
++ const struct media_pad *local,
++ const struct media_pad *remote, u32 flags)
++{
++ if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++ (flags & MEDIA_LNK_FL_ENABLED)) {
++ struct v4l2_subdev *sd;
++ struct stf_dvp_dev *dvp_dev;
++ struct vin_line *line;
++
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++
++ sd = media_entity_to_v4l2_subdev(entity);
++ dvp_dev = v4l2_get_subdevdata(sd);
++
++ sd = media_entity_to_v4l2_subdev(remote->entity);
++ line = v4l2_get_subdevdata(sd);
++ if (line->sdev_type == VIN_DEV_TYPE)
++ dvp_dev->s_type = SENSOR_VIN;
++ if (line->sdev_type == ISP_DEV_TYPE)
++ dvp_dev->s_type = SENSOR_ISP;
++ st_info(ST_DVP, "DVP device sensor type: %d\n", dvp_dev->s_type);
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops dvp_core_ops = {
++ .s_power = dvp_set_power,
++};
++
++static const struct v4l2_subdev_video_ops dvp_video_ops = {
++ .s_stream = dvp_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops dvp_pad_ops = {
++ .enum_mbus_code = dvp_enum_mbus_code,
++ .enum_frame_size = dvp_enum_frame_size,
++ .get_fmt = dvp_get_format,
++ .set_fmt = dvp_set_format,
++};
++
++static const struct v4l2_subdev_ops dvp_v4l2_ops = {
++ .core = &dvp_core_ops,
++ .video = &dvp_video_ops,
++ .pad = &dvp_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops dvp_v4l2_internal_ops = {
++ .open = dvp_init_formats,
++};
++
++static const struct media_entity_operations dvp_media_ops = {
++ .link_setup = dvp_link_setup,
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
++ struct v4l2_device *v4l2_dev)
++{
++ struct v4l2_subdev *sd = &dvp_dev->subdev;
++ struct media_pad *pads = dvp_dev->pads;
++ int ret;
++
++ v4l2_subdev_init(sd, &dvp_v4l2_ops);
++ sd->internal_ops = &dvp_v4l2_internal_ops;
++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++ STF_DVP_NAME, 0);
++ v4l2_set_subdevdata(sd, dvp_dev);
++
++ ret = dvp_init_formats(sd, NULL);
++ if (ret < 0) {
++ st_err(ST_DVP, "Failed to init format: %d\n", ret);
++ return ret;
++ }
++
++ pads[STF_DVP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++ pads[STF_DVP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->entity.ops = &dvp_media_ops;
++ ret = media_entity_pads_init(&sd->entity, STF_DVP_PADS_NUM, pads);
++ if (ret < 0) {
++ st_err(ST_DVP, "Failed to init media entity: %d\n", ret);
++ return ret;
++ }
++
++ ret = v4l2_device_register_subdev(v4l2_dev, sd);
++ if (ret < 0) {
++ st_err(ST_DVP, "Failed to register subdev: %d\n", ret);
++ goto err_sreg;
++ }
++
++ return 0;
++
++err_sreg:
++ media_entity_cleanup(&sd->entity);
++ return ret;
++}
++
++int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev)
++{
++ v4l2_device_unregister_subdev(&dvp_dev->subdev);
++ media_entity_cleanup(&dvp_dev->subdev.entity);
++ mutex_destroy(&dvp_dev->stream_lock);
++ return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
+@@ -0,0 +1,67 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_DVP_H
++#define STF_DVP_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_DVP_NAME "stf_dvp"
++
++#define STF_DVP_PAD_SINK 0
++#define STF_DVP_PAD_SRC 1
++#define STF_DVP_PADS_NUM 2
++
++struct dvp_format {
++ u32 code;
++ u8 bpp;
++};
++
++enum sensor_type;
++enum subdev_type;
++
++struct dvp_cfg {
++ unsigned int flags;
++ unsigned char bus_width;
++ unsigned char data_shift;
++};
++
++struct stf_dvp_dev;
++
++struct dvp_hw_ops {
++ int (*dvp_clk_enable)(struct stf_dvp_dev *dvp_dev);
++ int (*dvp_clk_disable)(struct stf_dvp_dev *dvp_dev);
++ int (*dvp_config_set)(struct stf_dvp_dev *dvp_dev);
++ int (*dvp_set_format)(struct stf_dvp_dev *dvp_dev,
++ u32 pix_width, u8 bpp);
++ int (*dvp_stream_set)(struct stf_dvp_dev *dvp_dev, int on);
++};
++
++struct stf_dvp_dev {
++ struct stfcamss *stfcamss;
++ struct dvp_cfg *dvp;
++ enum sensor_type s_type;
++ struct v4l2_subdev subdev;
++ struct media_pad pads[STF_DVP_PADS_NUM];
++ struct v4l2_mbus_framefmt fmt[STF_DVP_PADS_NUM];
++ const struct dvp_format *formats;
++ unsigned int nformats;
++ struct dvp_hw_ops *hw_ops;
++ struct mutex stream_lock;
++ int stream_count;
++};
++
++extern int stf_dvp_subdev_init(struct stfcamss *stfcamss);
++extern int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
++ struct v4l2_device *v4l2_dev);
++extern int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev);
++
++extern struct dvp_hw_ops dvp_ops;
++
++#endif /* STF_DVP_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
+@@ -0,0 +1,187 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++
++static int stf_dvp_clk_enable(struct stf_dvp_dev *dvp_dev)
++{
++ struct stfcamss *stfcamss = dvp_dev->stfcamss;
++
++ switch (dvp_dev->s_type) {
++ case SENSOR_VIN:
++ reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++ clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
++ clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++ stfcamss->sys_clk[STFCLK_DVP_INV].clk);
++ break;
++ case SENSOR_ISP:
++ clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
++ clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++ stfcamss->sys_clk[STFCLK_DVP_INV].clk);
++ break;
++ }
++
++ return 0;
++}
++
++static int stf_dvp_clk_disable(struct stf_dvp_dev *dvp_dev)
++{
++ struct stfcamss *stfcamss = dvp_dev->stfcamss;
++
++ switch (dvp_dev->s_type) {
++ case SENSOR_VIN:
++ clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++ stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++ reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++ break;
++ case SENSOR_ISP:
++ clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++ stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++ break;
++ }
++
++ return 0;
++}
++
++static int stf_dvp_config_set(struct stf_dvp_dev *dvp_dev)
++{
++
++ struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++ unsigned int flags = 0;
++ unsigned char data_shift = 0;
++ u32 polarities = 0;
++
++ if (!dvp_dev->dvp)
++ return -EINVAL;
++
++ flags = dvp_dev->dvp->flags;
++ data_shift = dvp_dev->dvp->data_shift;
++ st_info(ST_DVP, "%s, polarities = 0x%x, flags = 0x%x\n",
++ __func__, polarities, flags);
++
++ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++ polarities |= BIT(1);
++ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
++ polarities |= BIT(3);
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ U0_VIN_CNFG_DVP_HS_POS
++ | U0_VIN_CNFG_DVP_VS_POS,
++ polarities);
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
++
++ switch (data_shift) {
++ case 0:
++ data_shift = 0;
++ break;
++ case 2:
++ data_shift = 1;
++ break;
++ case 4:
++ data_shift = 2;
++ break;
++ case 6:
++ data_shift = 3;
++ break;
++ default:
++ data_shift = 0;
++ break;
++ };
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL,
++ data_shift << 15);
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++
++ return 0;
++}
++
++static int set_vin_axiwr_pix_ct(struct stf_vin_dev *vin, u8 bpp)
++{
++ u32 value = 0;
++ int cnfg_axiwr_pix_ct = 64 / bpp;
++
++ // need check
++ if (cnfg_axiwr_pix_ct == 2)
++ value = 1;
++ else if (cnfg_axiwr_pix_ct == 4)
++ value = 1;
++ else if (cnfg_axiwr_pix_ct == 8)
++ value = 0;
++ else
++ return 0;
++
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++ reg_set_bit(vin->sysctrl_base,
++ SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_PIX_CT,
++ value<<13);
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++
++ return cnfg_axiwr_pix_ct;
++
++}
++
++static int stf_dvp_set_format(struct stf_dvp_dev *dvp_dev,
++ u32 pix_width, u8 bpp)
++{
++ struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++ int val, pix_ct;
++
++ if (dvp_dev->s_type == SENSOR_VIN) {
++ pix_ct = set_vin_axiwr_pix_ct(vin, bpp);
++ val = (pix_width / pix_ct) - 1;
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
++ reg_set_bit(vin->sysctrl_base,
++ SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_PIX_CNT_END,
++ val << 2);
++ print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
++
++ }
++
++ return 0;
++}
++
++static int stf_dvp_stream_set(struct stf_dvp_dev *dvp_dev, int on)
++{
++ struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++
++ switch (dvp_dev->s_type) {
++ case SENSOR_VIN:
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ U0_VIN_CNFG_ISP_DVP_EN0,
++ 0);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
++ U0_VIN_CNFG_AXI_DVP_EN,
++ !!on<<2);
++ break;
++ case SENSOR_ISP:
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ U0_VIN_CNFG_ISP_DVP_EN0,
++ !!on<<5);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
++ U0_VIN_CNFG_AXI_DVP_EN,
++ 0);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ U0_VIN_CNFG_DVP_SWAP_EN,
++ 0);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++ U0_VIN_CNFG_GEN_EN_AXIRD,
++ 0);
++ break;
++ }
++
++ return 0;
++}
++
++struct dvp_hw_ops dvp_ops = {
++ .dvp_clk_enable = stf_dvp_clk_enable,
++ .dvp_clk_disable = stf_dvp_clk_disable,
++ .dvp_config_set = stf_dvp_config_set,
++ .dvp_set_format = stf_dvp_set_format,
++ .dvp_stream_set = stf_dvp_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_event.c
+@@ -0,0 +1,36 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/notifier.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++
++static ATOMIC_NOTIFIER_HEAD(vin_notifier_list);
++
++int vin_notifier_register(struct notifier_block *nb)
++{
++ return atomic_notifier_chain_register(&vin_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_register);
++
++void vin_notifier_unregister(struct notifier_block *nb)
++{
++ atomic_notifier_chain_unregister(&vin_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_unregister);
++
++int vin_notifier_call(unsigned long e, void *v)
++{
++ return atomic_notifier_call_chain(&vin_notifier_list, e, v);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_call);
++
++MODULE_AUTHOR("StarFive Technology Co., Ltd.");
++MODULE_DESCRIPTION("Starfive VIC video in notifier");
++MODULE_LICENSE("GPL");
++//MODULE_SUPPORTED_DEVICE("video");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -0,0 +1,1521 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include <linux/firmware.h>
++#include <linux/jh7110-isp.h>
++#include "stf_isp_ioctl.h"
++#include "stf_dmabuf.h"
++
++static int user_config_isp;
++static int isp_set_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel);
++
++static struct v4l2_rect *
++__isp_get_compose(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which);
++
++static struct v4l2_rect *
++__isp_get_crop(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which);
++
++static struct v4l2_rect *
++__isp_get_scale(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel);
++
++static struct v4l2_rect *
++__isp_get_itiws(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which);
++
++// sink format and raw format must one by one
++static const struct isp_format isp_formats_st7110_sink[] = {
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++static const struct isp_format isp_formats_st7110_raw[] = {
++ { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++ { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++ { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++ { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++};
++
++static const struct isp_format isp_formats_st7110_compat_10bit_raw[] = {
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++static const struct isp_format isp_formats_st7110_compat_8bit_raw[] = {
++ { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++ { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++ { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++ { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++};
++
++static const struct isp_format isp_formats_st7110_uo[] = {
++ { MEDIA_BUS_FMT_Y12_1X12, 8},
++};
++
++static const struct isp_format isp_formats_st7110_iti[] = {
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++ { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++ { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++ { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++ { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++ { MEDIA_BUS_FMT_Y12_1X12, 8},
++ { MEDIA_BUS_FMT_YUV8_1X24, 8},
++};
++
++static const struct isp_format_table isp_formats_st7110[] = {
++ { isp_formats_st7110_sink, ARRAY_SIZE(isp_formats_st7110_sink) }, /* pad 0 */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) }, /* pad 1 */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) }, /* pad 2 */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) }, /* pad 3 */
++ { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) }, /* pad 4 */
++ { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) }, /* pad 5 */
++ { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) }, /* pad 6 */
++ { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) }, /* pad 7 */
++};
++
++int stf_isp_subdev_init(struct stfcamss *stfcamss)
++{
++ struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++
++ isp_dev->sdev_type = ISP_DEV_TYPE;
++ isp_dev->hw_ops = &isp_ops;
++ isp_dev->stfcamss = stfcamss;
++ isp_dev->formats = isp_formats_st7110;
++ isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
++ mutex_init(&isp_dev->stream_lock);
++ mutex_init(&isp_dev->power_lock);
++ mutex_init(&isp_dev->setfile_lock);
++ atomic_set(&isp_dev->shadow_count, 0);
++ return 0;
++}
++
++/*
++ * ISP Controls.
++ */
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct stf_isp_dev,
++ ctrls.handler)->subdev;
++}
++
++static u64 isp_calc_pixel_rate(struct stf_isp_dev *isp_dev)
++{
++ u64 rate = 0;
++
++ return rate;
++}
++
++static int isp_set_ctrl_hue(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_contrast(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_saturation(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_white_balance(struct stf_isp_dev *isp_dev, int awb)
++{
++ struct isp_ctrls *ctrls = &isp_dev->ctrls;
++ int ret = 0;
++
++ if (!awb && (ctrls->red_balance->is_new
++ || ctrls->blue_balance->is_new)) {
++ u16 red = (u16)ctrls->red_balance->val;
++ u16 blue = (u16)ctrls->blue_balance->val;
++
++ st_debug(ST_ISP, "red = 0x%x, blue = 0x%x\n", red, blue);
++ //isp_dev->hw_ops->isp_set_awb_r_gain(isp_dev, red);
++ //if (ret)
++ // return ret;
++ //isp_dev->hw_ops->isp_set_awb_b_gain(isp_dev, blue);
++ }
++
++ return ret;
++}
++
++static int isp_set_ctrl_exposure(struct stf_isp_dev *isp_dev,
++ enum v4l2_exposure_auto_type auto_exposure)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_gain(struct stf_isp_dev *isp_dev, bool auto_gain)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++ "Disabled",
++ "Color bars",
++ "Color bars w/ rolling bar",
++ "Color squares",
++ "Color squares w/ rolling bar",
++};
++
++#define ISP_TEST_ENABLE BIT(7)
++#define ISP_TEST_ROLLING BIT(6) /* rolling horizontal bar */
++#define ISP_TEST_TRANSPARENT BIT(5)
++#define ISP_TEST_SQUARE_BW BIT(4) /* black & white squares */
++#define ISP_TEST_BAR_STANDARD (0 << 2)
++#define ISP_TEST_BAR_VERT_CHANGE_1 (1 << 2)
++#define ISP_TEST_BAR_HOR_CHANGE (2 << 2)
++#define ISP_TEST_BAR_VERT_CHANGE_2 (3 << 2)
++#define ISP_TEST_BAR (0 << 0)
++#define ISP_TEST_RANDOM (1 << 0)
++#define ISP_TEST_SQUARE (2 << 0)
++#define ISP_TEST_BLACK (3 << 0)
++
++static const u8 test_pattern_val[] = {
++ 0,
++ ISP_TEST_ENABLE | ISP_TEST_BAR_VERT_CHANGE_1 |
++ ISP_TEST_BAR,
++ ISP_TEST_ENABLE | ISP_TEST_ROLLING |
++ ISP_TEST_BAR_VERT_CHANGE_1 | ISP_TEST_BAR,
++ ISP_TEST_ENABLE | ISP_TEST_SQUARE,
++ ISP_TEST_ENABLE | ISP_TEST_ROLLING | ISP_TEST_SQUARE,
++};
++
++static int isp_set_ctrl_test_pattern(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ // return isp_write_reg(isp_dev, ISP_REG_PRE_ISP_TEST_SET1,
++ // test_pattern_val[value]);
++ return ret;
++}
++
++static int isp_set_ctrl_light_freq(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_hflip(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_set_ctrl_vflip(struct stf_isp_dev *isp_dev, int value)
++{
++ int ret = 0;
++
++ return ret;
++}
++
++static int isp_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ break;
++ }
++
++ return 0;
++}
++
++static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored right after power-up.
++ */
++ mutex_lock(&isp_dev->power_lock);
++ if (isp_dev->power_count == 0) {
++ mutex_unlock(&isp_dev->power_lock);
++ return 0;
++ }
++ mutex_unlock(&isp_dev->power_lock);
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ ret = isp_set_ctrl_gain(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ ret = isp_set_ctrl_exposure(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = isp_set_ctrl_white_balance(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_HUE:
++ ret = isp_set_ctrl_hue(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_CONTRAST:
++ ret = isp_set_ctrl_contrast(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_SATURATION:
++ ret = isp_set_ctrl_saturation(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = isp_set_ctrl_test_pattern(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ ret = isp_set_ctrl_light_freq(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = isp_set_ctrl_hflip(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_VFLIP:
++ ret = isp_set_ctrl_vflip(isp_dev, ctrl->val);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_WB_SETTING:
++ break;
++ case V4L2_CID_USER_JH7110_ISP_CAR_SETTING:
++ break;
++ case V4L2_CID_USER_JH7110_ISP_CCM_SETTING:
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops isp_ctrl_ops = {
++ .g_volatile_ctrl = isp_g_volatile_ctrl,
++ .s_ctrl = isp_s_ctrl,
++};
++
++struct v4l2_ctrl_config isp_ctrl[] = {
++ [0] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "WB Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_WB_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_wb_setting),
++ .flags = 0,
++ },
++ [1] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "Car Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_CAR_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_car_setting),
++ .flags = 0,
++ },
++ [2] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "CCM Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_CCM_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_ccm_setting),
++ .flags = 0,
++ },
++};
++
++static int isp_init_controls(struct stf_isp_dev *isp_dev)
++{
++ const struct v4l2_ctrl_ops *ops = &isp_ctrl_ops;
++ struct isp_ctrls *ctrls = &isp_dev->ctrls;
++ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++ int ret;
++ int i;
++
++ v4l2_ctrl_handler_init(hdl, 32);
++
++ /* Clock related controls */
++ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++ 0, INT_MAX, 1,
++ isp_calc_pixel_rate(isp_dev));
++
++ /* Auto/manual white balance */
++ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, 1, 1, 1);
++ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++ 0, 4095, 1, 0);
++ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++ 0, 4095, 1, 0);
++ /* Auto/manual exposure */
++ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_EXPOSURE_AUTO,
++ V4L2_EXPOSURE_MANUAL, 0,
++ V4L2_EXPOSURE_AUTO);
++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++ 0, 65535, 1, 0);
++ /* Auto/manual gain */
++ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++ 0, 1, 1, 1);
++ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++ 0, 1023, 1, 0);
++
++ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++ 0, 255, 1, 64);
++ ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++ 0, 359, 1, 0);
++ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++ 0, 255, 1, 0);
++ ctrls->test_pattern =
++ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(test_pattern_menu) - 1,
++ 0, 0, test_pattern_menu);
++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++ 0, 1, 1, 0);
++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++ 0, 1, 1, 0);
++
++ ctrls->light_freq =
++ v4l2_ctrl_new_std_menu(hdl, ops,
++ V4L2_CID_POWER_LINE_FREQUENCY,
++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++ for (i = 0; i < ARRAY_SIZE(isp_ctrl); i++)
++ v4l2_ctrl_new_custom(hdl, &isp_ctrl[i], NULL);
++
++
++ if (hdl->error) {
++ ret = hdl->error;
++ goto free_ctrls;
++ }
++
++ ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++ isp_dev->subdev.ctrl_handler = hdl;
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(hdl);
++ return ret;
++}
++
++static int isp_set_power(struct v4l2_subdev *sd, int on)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++
++ st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
++ mutex_lock(&isp_dev->power_lock);
++ if (on) {
++ if (isp_dev->power_count == 0)
++ st_debug(ST_ISP, "turn on isp\n");
++ isp_dev->power_count++;
++ } else {
++ if (isp_dev->power_count == 0)
++ goto exit;
++ isp_dev->power_count--;
++ }
++exit:
++ mutex_unlock(&isp_dev->power_lock);
++
++ return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__isp_get_format(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which)
++{
++
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_format(&isp_dev->subdev, state, pad);
++
++ return &isp_dev->fmt[pad];
++}
++
++static int isp_get_interface_type(struct media_entity *entity)
++{
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad = &entity->pads[0];
++
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ return -EINVAL;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ return -EINVAL;
++
++ subdev = media_entity_to_v4l2_subdev(pad->entity);
++
++ st_debug(ST_ISP, "interface subdev name %s\n", subdev->name);
++ if (!strncmp(subdev->name, STF_CSI_NAME, strlen(STF_CSI_NAME)))
++ return CSI_SENSOR;
++ if (!strncmp(subdev->name, STF_DVP_NAME, strlen(STF_DVP_NAME)))
++ return DVP_SENSOR;
++ return -EINVAL;
++}
++
++static int isp_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ int ret = 0, interface_type;
++ struct v4l2_mbus_framefmt *fmt;
++ struct v4l2_event src_ch = { 0 };
++
++ fmt = __isp_get_format(isp_dev, NULL, STF_ISP_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE);
++ mutex_lock(&isp_dev->stream_lock);
++ if (enable) {
++ if (isp_dev->stream_count == 0) {
++ isp_dev->hw_ops->isp_clk_enable(isp_dev);
++ if (!user_config_isp)
++ isp_dev->hw_ops->isp_config_set(isp_dev);
++ interface_type = isp_get_interface_type(&sd->entity);
++ if (interface_type < 0) {
++ st_err(ST_ISP, "%s, pipeline not config\n", __func__);
++ goto exit;
++ }
++ isp_dev->hw_ops->isp_set_format(isp_dev,
++ isp_dev->rect, fmt->code, interface_type);
++ isp_dev->hw_ops->isp_reset(isp_dev);
++ isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
++ user_config_isp = 0;
++ }
++ isp_dev->stream_count++;
++ } else {
++ if (isp_dev->stream_count == 0)
++ goto exit;
++ if (isp_dev->stream_count == 1) {
++ isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
++ isp_dev->hw_ops->isp_clk_disable(isp_dev);
++ }
++ isp_dev->stream_count--;
++ }
++ src_ch.type = V4L2_EVENT_SOURCE_CHANGE,
++ src_ch.u.src_change.changes = isp_dev->stream_count,
++
++ v4l2_subdev_notify_event(sd, &src_ch);
++exit:
++ mutex_unlock(&isp_dev->stream_lock);
++
++ mutex_lock(&isp_dev->power_lock);
++ /* restore controls */
++ if (enable && isp_dev->power_count == 1) {
++ mutex_unlock(&isp_dev->power_lock);
++ ret = v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
++ } else
++ mutex_unlock(&isp_dev->power_lock);
++
++ return ret;
++}
++
++/*Try to match sensor format with sink, and then get the index as default.*/
++static int isp_match_sensor_format_get_index(struct stf_isp_dev *isp_dev)
++{
++ int ret, idx;
++ struct media_entity *sensor;
++ struct v4l2_subdev *subdev;
++ struct v4l2_subdev_format fmt;
++ const struct isp_format_table *formats;
++
++ if (!isp_dev)
++ return -EINVAL;
++
++ sensor = stfcamss_find_sensor(&isp_dev->subdev.entity);
++ if (!sensor)
++ return -EINVAL;
++
++ subdev = media_entity_to_v4l2_subdev(sensor);
++ st_debug(ST_ISP, "Found sensor = %s\n", sensor->name);
++
++ fmt.pad = 0;
++ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
++ if (ret) {
++ st_warn(ST_ISP, "Sonser get format failed !!\n");
++ return -EINVAL;
++ }
++
++ st_debug(ST_ISP, "Got sensor format 0x%x !!\n", fmt.format.code);
++
++ formats = &isp_dev->formats[0]; /* isp sink format */
++ for (idx = 0; idx < formats->nfmts; idx++) {
++ if (formats->fmts[idx].code == fmt.format.code) {
++ st_info(ST_ISP,
++ "Match sensor format to isp_formats_st7110_sink index %d !!\n",
++ idx);
++ return idx;
++ }
++ }
++ return -ERANGE;
++}
++
++static int isp_match_format_get_index(const struct isp_format_table *f_table,
++ __u32 mbus_code,
++ unsigned int pad)
++{
++ int i;
++
++ for (i = 0; i < f_table->nfmts; i++) {
++ if (mbus_code == f_table->fmts[i].code) {
++ break;
++ } else {
++ if (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y) {
++ if (mbus_code == (isp_formats_st7110_compat_10bit_raw[i].code ||
++ isp_formats_st7110_compat_8bit_raw[i].code))
++ break;
++ }
++ }
++ }
++
++ return i;
++}
++
++static void isp_try_format(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ struct v4l2_mbus_framefmt *fmt,
++ enum v4l2_subdev_format_whence which)
++{
++ const struct isp_format_table *formats;
++ unsigned int i;
++ u32 code = fmt->code;
++ u32 bpp;
++
++ if (pad == STF_ISP_PAD_SINK) {
++ /* Set format on sink pad */
++
++ formats = &isp_dev->formats[pad];
++ fmt->width = clamp_t(u32,
++ fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++ fmt->height &= ~0x1;
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++ } else {
++ formats = &isp_dev->formats[pad];
++ }
++
++ i = isp_match_format_get_index(formats, fmt->code, pad);
++ st_debug(ST_ISP, "%s pad=%d, code=%x isp_match_format_get_index = %d\n",
++ __func__, pad, code, i);
++
++ if (i >= formats->nfmts &&
++ (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y)) {
++ int sensor_idx;
++
++ sensor_idx = isp_match_sensor_format_get_index(isp_dev);
++ if (sensor_idx)
++ i = sensor_idx;
++ }
++
++ if (pad != STF_ISP_PAD_SINK)
++ *fmt = *__isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which);
++
++ if (i >= formats->nfmts) {
++ fmt->code = formats->fmts[0].code;
++ bpp = formats->fmts[0].bpp;
++ st_info(ST_ISP, "Use default index 0 format = 0x%x\n", fmt->code);
++ } else {
++ // sink format and raw format must one by one
++ if (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y) {
++ fmt->code = formats->fmts[i].code;
++ bpp = formats->fmts[i].bpp;
++ st_info(ST_ISP, "Use mapping format from sink index %d = 0x%x\n",
++ i, fmt->code);
++ } else {
++ fmt->code = code;
++ bpp = formats->fmts[i].bpp;
++ st_info(ST_ISP, "Use input format = 0x%x\n", fmt->code);
++ }
++ }
++
++ switch (pad) {
++ case STF_ISP_PAD_SINK:
++ break;
++ case STF_ISP_PAD_SRC:
++ isp_dev->rect[ISP_COMPOSE].bpp = bpp;
++ break;
++ case STF_ISP_PAD_SRC_SS0:
++ isp_dev->rect[ISP_SCALE_SS0].bpp = bpp;
++ break;
++ case STF_ISP_PAD_SRC_SS1:
++ isp_dev->rect[ISP_SCALE_SS1].bpp = bpp;
++ break;
++ case STF_ISP_PAD_SRC_ITIW:
++ case STF_ISP_PAD_SRC_ITIR:
++ isp_dev->rect[ISP_ITIWS].bpp = bpp;
++ break;
++ case STF_ISP_PAD_SRC_RAW:
++ isp_dev->rect[ISP_CROP].bpp = bpp;
++ break;
++ case STF_ISP_PAD_SRC_SCD_Y:
++ break;
++ }
++}
++
++static int isp_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ const struct isp_format_table *formats;
++
++ if (code->index >= isp_dev->formats[code->pad].nfmts)
++ return -EINVAL;
++
++ formats = &isp_dev->formats[code->pad];
++ code->code = formats->fmts[code->index].code;
++ code->flags = 0;
++
++ return 0;
++}
++
++static int isp_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt format;
++
++ if (fse->index != 0)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = 1;
++ format.height = 1;
++ isp_try_format(isp_dev, state, fse->pad, &format, fse->which);
++ fse->min_width = format.width;
++ fse->min_height = format.height;
++
++ if (format.code != fse->code)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = -1;
++ format.height = -1;
++ isp_try_format(isp_dev, state, fse->pad, &format, fse->which);
++ fse->max_width = format.width;
++ fse->max_height = format.height;
++
++ return 0;
++}
++
++static int isp_get_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ fmt->format = *format;
++
++ return 0;
++}
++
++static int isp_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++ struct v4l2_subdev_selection sel = { 0 };
++ struct v4l2_rect *rect = NULL;
++ int ret;
++
++ st_debug(ST_ISP, "%s pad=%d, code=%x, which=%d\n",
++ __func__, fmt->reserved[0], fmt->format.code, fmt->which);
++ format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count) {
++ fmt->format = *format;
++ if (fmt->reserved[0] != 0) {
++ sel.which = fmt->which;
++ sel.pad = fmt->reserved[0];
++
++ switch (fmt->reserved[0]) {
++ case STF_ISP_PAD_SRC:
++ rect = __isp_get_compose(isp_dev, state, fmt->which);
++ break;
++ case STF_ISP_PAD_SRC_SS0:
++ case STF_ISP_PAD_SRC_SS1:
++ rect = __isp_get_scale(isp_dev, state, &sel);
++ break;
++ case STF_ISP_PAD_SRC_ITIW:
++ case STF_ISP_PAD_SRC_ITIR:
++ rect = __isp_get_itiws(isp_dev, state, fmt->which);
++ break;
++ case STF_ISP_PAD_SRC_RAW:
++ case STF_ISP_PAD_SRC_SCD_Y:
++ rect = __isp_get_crop(isp_dev, state, fmt->which);
++ break;
++ default:
++ break;
++ }
++ if (rect != NULL) {
++ fmt->format.width = rect->width;
++ fmt->format.height = rect->height;
++ }
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++ goto out;
++ } else {
++ isp_try_format(isp_dev, state, fmt->pad, &fmt->format, fmt->which);
++ *format = fmt->format;
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++
++ /* Propagate the format from sink to source */
++ if (fmt->pad == STF_ISP_PAD_SINK) {
++ /* Reset sink pad compose selection */
++ sel.which = fmt->which;
++ sel.pad = STF_ISP_PAD_SINK;
++ sel.target = V4L2_SEL_TGT_CROP;
++ sel.r.width = fmt->format.width;
++ sel.r.height = fmt->format.height;
++ ret = isp_set_selection(sd, state, &sel);
++ if (ret < 0)
++ return ret;
++ }
++
++out:
++ return 0;
++}
++
++static struct v4l2_rect *
++__isp_get_compose(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_compose(&isp_dev->subdev, state,
++ STF_ISP_PAD_SINK);
++
++
++ return &isp_dev->rect[ISP_COMPOSE].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_crop(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_crop(&isp_dev->subdev, state,
++ STF_ISP_PAD_SINK);
++
++ return &isp_dev->rect[ISP_CROP].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_scale(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel)
++{
++ int pad;
++
++ if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_compose(&isp_dev->subdev, state,
++ STF_ISP_PAD_SINK);
++ if (sel->pad != STF_ISP_PAD_SRC_SS0 && sel->pad != STF_ISP_PAD_SRC_SS1)
++ return NULL;
++
++ pad = sel->pad == STF_ISP_PAD_SRC_SS0 ? ISP_SCALE_SS0 : ISP_SCALE_SS1;
++ return &isp_dev->rect[pad].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_itiws(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_crop(&isp_dev->subdev, state, STF_ISP_PAD_SINK);
++
++ return &isp_dev->rect[ISP_ITIWS].rect;
++}
++
++static void isp_try_crop(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_rect *rect,
++ enum v4l2_subdev_format_whence which)
++{
++ struct v4l2_mbus_framefmt *fmt;
++
++ fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which);
++
++ if (rect->width > fmt->width)
++ rect->width = fmt->width;
++
++ if (rect->width + rect->left > fmt->width)
++ rect->left = fmt->width - rect->width;
++
++ if (rect->height > fmt->height)
++ rect->height = fmt->height;
++
++ if (rect->height + rect->top > fmt->height)
++ rect->top = fmt->height - rect->height;
++
++ if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++ rect->left = 0;
++ rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++ }
++
++ if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++ rect->top = 0;
++ rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++ }
++ rect->height &= ~0x1;
++}
++
++static void isp_try_compose(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_rect *rect,
++ enum v4l2_subdev_format_whence which)
++{
++ struct v4l2_rect *crop;
++
++ crop = __isp_get_crop(isp_dev, state, which);
++
++ if (rect->width > crop->width)
++ rect->width = crop->width;
++
++ if (rect->height > crop->height)
++ rect->height = crop->height;
++
++ if (crop->width > rect->width * SCALER_RATIO_MAX)
++ rect->width = (crop->width + SCALER_RATIO_MAX - 1) /
++ SCALER_RATIO_MAX;
++
++ if (crop->height > rect->height * SCALER_RATIO_MAX)
++ rect->height = (crop->height + SCALER_RATIO_MAX - 1) /
++ SCALER_RATIO_MAX;
++
++ if (rect->width < STFCAMSS_FRAME_MIN_WIDTH)
++ rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++
++ if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT)
++ rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++ rect->height &= ~0x1;
++}
++
++static void isp_try_scale(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_rect *rect,
++ enum v4l2_subdev_format_whence which)
++{
++ struct v4l2_rect *compose;
++
++ compose = __isp_get_compose(isp_dev, state, which);
++
++ if (rect->width > compose->width)
++ rect->width = compose->width;
++
++ if (rect->width + rect->left > compose->width)
++ rect->left = compose->width - rect->width;
++
++ if (rect->height > compose->height)
++ rect->height = compose->height;
++
++ if (rect->height + rect->top > compose->height)
++ rect->top = compose->height - rect->height;
++
++ if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++ rect->left = 0;
++ rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++ }
++
++ if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++ rect->top = 0;
++ rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++ }
++ rect->height &= ~0x1;
++}
++
++static void isp_try_itiws(struct stf_isp_dev *isp_dev,
++ struct v4l2_subdev_state *state,
++ struct v4l2_rect *rect,
++ enum v4l2_subdev_format_whence which)
++{
++ struct v4l2_rect *crop;
++
++ crop = __isp_get_crop(isp_dev, state, which);
++
++ if (rect->width > crop->width)
++ rect->width = crop->width;
++
++ if (rect->width + rect->left > crop->width)
++ rect->left = crop->width - rect->width;
++
++ if (rect->height > crop->height)
++ rect->height = crop->height;
++
++ if (rect->height + rect->top > crop->height)
++ rect->top = crop->height - rect->height;
++
++ if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++ rect->left = 0;
++ rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++ }
++
++ if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++ rect->top = 0;
++ rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++ }
++ rect->height &= ~0x1;
++}
++
++static int isp_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_subdev_format fmt = { 0 };
++ struct v4l2_rect *rect;
++ int ret;
++
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP_BOUNDS:
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ fmt.pad = sel->pad;
++ fmt.which = sel->which;
++ ret = isp_get_format(sd, state, &fmt);
++ if (ret < 0)
++ return ret;
++
++ sel->r.left = 0;
++ sel->r.top = 0;
++ sel->r.width = fmt.format.width;
++ sel->r.height = fmt.format.height;
++ break;
++ case V4L2_SEL_TGT_CROP:
++ rect = __isp_get_crop(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++
++ sel->r = *rect;
++ break;
++ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
++ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
++ if (sel->pad > STF_ISP_PAD_SRC_ITIR)
++ return -EINVAL;
++ rect = __isp_get_crop(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++
++ sel->r.left = rect->left;
++ sel->r.top = rect->top;
++ sel->r.width = rect->width;
++ sel->r.height = rect->height;
++ break;
++ case V4L2_SEL_TGT_COMPOSE:
++ if (sel->pad > STF_ISP_PAD_SRC_ITIR)
++ return -EINVAL;
++ if (sel->pad == STF_ISP_PAD_SRC_SS0
++ || sel->pad == STF_ISP_PAD_SRC_SS1) {
++ rect = __isp_get_scale(isp_dev, state, sel);
++ if (rect == NULL)
++ return -EINVAL;
++ } else if (sel->pad == STF_ISP_PAD_SRC_ITIW
++ || sel->pad == STF_ISP_PAD_SRC_ITIR) {
++ rect = __isp_get_itiws(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++ } else {
++ rect = __isp_get_compose(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++ }
++ sel->r = *rect;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n",
++ __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
++ return 0;
++}
++
++static int isp_set_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_selection *sel)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct v4l2_rect *rect;
++ int ret = 0;
++
++ if (sel->target == V4L2_SEL_TGT_COMPOSE &&
++ ((sel->pad == STF_ISP_PAD_SINK)
++ || (sel->pad == STF_ISP_PAD_SRC))) {
++ struct v4l2_subdev_format fmt = { 0 };
++ int i;
++
++ rect = __isp_get_compose(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count) {
++ sel->r = *rect;
++ mutex_unlock(&isp_dev->stream_lock);
++ ret = 0;
++ goto out;
++ } else {
++ isp_try_compose(isp_dev, state, &sel->r, sel->which);
++ *rect = sel->r;
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++
++ /* Reset source pad format width and height */
++ fmt.which = sel->which;
++ fmt.pad = STF_ISP_PAD_SRC;
++ ret = isp_get_format(sd, state, &fmt);
++ if (ret < 0)
++ return ret;
++
++ fmt.format.width = rect->width;
++ fmt.format.height = rect->height;
++ ret = isp_set_format(sd, state, &fmt);
++
++ /* Reset scale */
++ for (i = STF_ISP_PAD_SRC_SS0; i <= STF_ISP_PAD_SRC_ITIR; i++) {
++ struct v4l2_subdev_selection scale = { 0 };
++
++ scale.which = sel->which;
++ scale.target = V4L2_SEL_TGT_COMPOSE;
++ scale.r = *rect;
++ scale.pad = i;
++ ret = isp_set_selection(sd, state, &scale);
++ }
++ } else if (sel->target == V4L2_SEL_TGT_COMPOSE
++ && ((sel->pad == STF_ISP_PAD_SRC_SS0)
++ || (sel->pad == STF_ISP_PAD_SRC_SS1))) {
++ struct v4l2_subdev_format fmt = { 0 };
++
++ rect = __isp_get_scale(isp_dev, state, sel);
++ if (rect == NULL)
++ return -EINVAL;
++
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count) {
++ sel->r = *rect;
++ mutex_unlock(&isp_dev->stream_lock);
++ ret = 0;
++ goto out;
++ } else {
++ isp_try_scale(isp_dev, state, &sel->r, sel->which);
++ *rect = sel->r;
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++
++ /* Reset source pad format width and height */
++ fmt.which = sel->which;
++ fmt.pad = sel->pad;
++ ret = isp_get_format(sd, state, &fmt);
++ if (ret < 0)
++ return ret;
++
++ fmt.format.width = rect->width;
++ fmt.format.height = rect->height;
++ ret = isp_set_format(sd, state, &fmt);
++ } else if (sel->target == V4L2_SEL_TGT_COMPOSE
++ && ((sel->pad == STF_ISP_PAD_SRC_ITIW)
++ || (sel->pad == STF_ISP_PAD_SRC_ITIR))) {
++ struct v4l2_subdev_format fmt = { 0 };
++
++ rect = __isp_get_itiws(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count) {
++ sel->r = *rect;
++ mutex_unlock(&isp_dev->stream_lock);
++ ret = 0;
++ goto out;
++ } else {
++ isp_try_itiws(isp_dev, state, &sel->r, sel->which);
++ *rect = sel->r;
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++
++ /* Reset source pad format width and height */
++ fmt.which = sel->which;
++ fmt.pad = sel->pad;
++ ret = isp_get_format(sd, state, &fmt);
++ if (ret < 0)
++ return ret;
++
++ fmt.format.width = rect->width;
++ fmt.format.height = rect->height;
++ ret = isp_set_format(sd, state, &fmt);
++ } else if (sel->target == V4L2_SEL_TGT_CROP) {
++ struct v4l2_subdev_selection compose = { 0 };
++ int i;
++
++ rect = __isp_get_crop(isp_dev, state, sel->which);
++ if (rect == NULL)
++ return -EINVAL;
++
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count) {
++ sel->r = *rect;
++ mutex_unlock(&isp_dev->stream_lock);
++ ret = 0;
++ goto out;
++ } else {
++ isp_try_crop(isp_dev, state, &sel->r, sel->which);
++ *rect = sel->r;
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++
++ /* Reset source compose selection */
++ compose.which = sel->which;
++ compose.target = V4L2_SEL_TGT_COMPOSE;
++ compose.r.width = rect->width;
++ compose.r.height = rect->height;
++ compose.pad = STF_ISP_PAD_SINK;
++ ret = isp_set_selection(sd, state, &compose);
++
++ /* Reset source pad format width and height */
++ for (i = STF_ISP_PAD_SRC_RAW; i < STF_ISP_PAD_MAX; i++) {
++ struct v4l2_subdev_format fmt = { 0 };
++
++ fmt.which = sel->which;
++ fmt.pad = i;
++ ret = isp_get_format(sd, state, &fmt);
++ if (ret < 0)
++ return ret;
++
++ fmt.format.width = rect->width;
++ fmt.format.height = rect->height;
++ ret = isp_set_format(sd, state, &fmt);
++ }
++ } else {
++ ret = -EINVAL;
++ }
++
++ st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n",
++ __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
++out:
++ return ret;
++}
++
++static int isp_init_formats(struct v4l2_subdev *sd,
++ struct v4l2_subdev_fh *fh)
++{
++ struct v4l2_subdev_format format = {
++ .pad = STF_ISP_PAD_SINK,
++ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++ V4L2_SUBDEV_FORMAT_ACTIVE,
++ .format = {
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .width = 1920,
++ .height = 1080
++ }
++ };
++
++ return isp_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int isp_link_setup(struct media_entity *entity,
++ const struct media_pad *local,
++ const struct media_pad *remote, u32 flags)
++{
++ if (flags & MEDIA_LNK_FL_ENABLED)
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++ return 0;
++}
++
++static int stf_isp_load_setfile(struct stf_isp_dev *isp_dev, char *file_name)
++{
++ struct device *dev = isp_dev->stfcamss->dev;
++ const struct firmware *fw;
++ u8 *buf = NULL;
++ int *regval_num;
++ int ret;
++
++ st_debug(ST_ISP, "%s, file_name %s\n", __func__, file_name);
++ ret = request_firmware(&fw, file_name, dev);
++ if (ret < 0) {
++ st_err(ST_ISP, "firmware request failed (%d)\n", ret);
++ return ret;
++ }
++ buf = devm_kzalloc(dev, fw->size, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++ memcpy(buf, fw->data, fw->size);
++
++ mutex_lock(&isp_dev->setfile_lock);
++ if (isp_dev->setfile.state == 1)
++ devm_kfree(dev, isp_dev->setfile.data);
++ isp_dev->setfile.data = buf;
++ isp_dev->setfile.size = fw->size;
++ isp_dev->setfile.state = 1;
++ regval_num = (int *)&buf[fw->size - sizeof(unsigned int)];
++ isp_dev->setfile.settings.regval_num = *regval_num;
++ isp_dev->setfile.settings.regval = (struct regval_t *)buf;
++ mutex_unlock(&isp_dev->setfile_lock);
++
++ st_debug(ST_ISP, "stf_isp setfile loaded size: %zu B, reg_nul: %d\n",
++ fw->size, isp_dev->setfile.settings.regval_num);
++
++ release_firmware(fw);
++ return ret;
++}
++
++static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++ struct device *dev = isp_dev->stfcamss->dev;
++ int ret = -ENOIOCTLCMD;
++
++ switch (cmd) {
++ case VIDIOC_STFISP_LOAD_FW: {
++ struct stfisp_fw_info *fw_info = arg;
++
++ if (IS_ERR(fw_info)) {
++ st_err(ST_ISP, "fw_info failed, params invaild\n");
++ return -EINVAL;
++ }
++
++ ret = stf_isp_load_setfile(isp_dev, fw_info->filename);
++ break;
++ }
++ case VIDIOC_STF_DMABUF_ALLOC:
++ case VIDIOC_STF_DMABUF_FREE:
++ ret = stf_dmabuf_ioctl(dev, cmd, arg);
++ break;
++ case VIDIOC_STFISP_GET_REG:
++ ret = isp_dev->hw_ops->isp_reg_read(isp_dev, arg);
++ break;
++ case VIDIOC_STFISP_SET_REG:
++ ret = isp_dev->hw_ops->isp_reg_write(isp_dev, arg);
++ break;
++ case VIDIOC_STFISP_SHADOW_LOCK:
++ if (atomic_add_unless(&isp_dev->shadow_count, 1, 1))
++ ret = 0;
++ else
++ ret = -EBUSY;
++ st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++ break;
++ case VIDIOC_STFISP_SHADOW_UNLOCK:
++ if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
++ ret = -EINVAL;
++ else
++ ret = 0;
++ st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++ break;
++ case VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER:
++ {
++ isp_dev->hw_ops->isp_shadow_trigger(isp_dev);
++ if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
++ ret = -EINVAL;
++ else
++ ret = 0;
++ st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++ }
++ break;
++ case VIDIOC_STFISP_SET_USER_CONFIG_ISP:
++ st_debug(ST_ISP, "%s, %d set user_config_isp\n", __func__, __LINE__);
++ user_config_isp = 1;
++ break;
++ default:
++ break;
++ }
++ return ret;
++}
++
++int isp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++
++ st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
++ while (atomic_dec_if_positive(&isp_dev->shadow_count) > 0)
++ st_warn(ST_ISP, "user not unlocked the shadow lock, driver unlock it!\n");
++
++ return 0;
++}
++
++static int stf_isp_subscribe_event(struct v4l2_subdev *sd,
++ struct v4l2_fh *fh,
++ struct v4l2_event_subscription *sub)
++{
++ switch (sub->type) {
++ case V4L2_EVENT_SOURCE_CHANGE:
++ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
++ case V4L2_EVENT_CTRL:
++ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
++ default:
++ st_debug(ST_ISP, "unspport subscribe_event\n");
++ return -EINVAL;
++ }
++}
++
++static const struct v4l2_subdev_core_ops isp_core_ops = {
++ .s_power = isp_set_power,
++ .ioctl = stf_isp_ioctl,
++ .log_status = v4l2_ctrl_subdev_log_status,
++ // .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .subscribe_event = stf_isp_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops isp_video_ops = {
++ .s_stream = isp_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops isp_pad_ops = {
++ .enum_mbus_code = isp_enum_mbus_code,
++ .enum_frame_size = isp_enum_frame_size,
++ .get_fmt = isp_get_format,
++ .set_fmt = isp_set_format,
++ .get_selection = isp_get_selection,
++ .set_selection = isp_set_selection,
++};
++
++static const struct v4l2_subdev_ops isp_v4l2_ops = {
++ .core = &isp_core_ops,
++ .video = &isp_video_ops,
++ .pad = &isp_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops isp_v4l2_internal_ops = {
++ .open = isp_init_formats,
++ .close = isp_close,
++};
++
++static const struct media_entity_operations isp_media_ops = {
++ .link_setup = isp_link_setup,
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_isp_register(struct stf_isp_dev *isp_dev,
++ struct v4l2_device *v4l2_dev)
++{
++ struct v4l2_subdev *sd = &isp_dev->subdev;
++ struct media_pad *pads = isp_dev->pads;
++ int ret;
++
++ v4l2_subdev_init(sd, &isp_v4l2_ops);
++ sd->internal_ops = &isp_v4l2_internal_ops;
++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++ STF_ISP_NAME, 0);
++ v4l2_set_subdevdata(sd, isp_dev);
++
++ ret = isp_init_formats(sd, NULL);
++ if (ret < 0) {
++ st_err(ST_ISP, "Failed to init format: %d\n", ret);
++ return ret;
++ }
++
++ pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++ pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_SS0].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_SS1].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_ITIW].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_ITIR].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_RAW].flags = MEDIA_PAD_FL_SOURCE;
++ pads[STF_ISP_PAD_SRC_SCD_Y].flags = MEDIA_PAD_FL_SOURCE;
++
++ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->entity.ops = &isp_media_ops;
++ ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads);
++ if (ret < 0) {
++ st_err(ST_ISP, "Failed to init media entity: %d\n", ret);
++ return ret;
++ }
++
++ ret = isp_init_controls(isp_dev);
++ if (ret)
++ goto err_sreg;
++
++ ret = v4l2_device_register_subdev(v4l2_dev, sd);
++ if (ret < 0) {
++ st_err(ST_ISP, "Failed to register subdev: %d\n", ret);
++ goto free_ctrls;
++ }
++
++ return 0;
++
++free_ctrls:
++ v4l2_ctrl_handler_free(&isp_dev->ctrls.handler);
++err_sreg:
++ media_entity_cleanup(&sd->entity);
++ return ret;
++}
++
++int stf_isp_unregister(struct stf_isp_dev *isp_dev)
++{
++ v4l2_device_unregister_subdev(&isp_dev->subdev);
++ media_entity_cleanup(&isp_dev->subdev.entity);
++ v4l2_ctrl_handler_free(&isp_dev->ctrls.handler);
++ mutex_destroy(&isp_dev->stream_lock);
++ mutex_destroy(&isp_dev->power_lock);
++ mutex_destroy(&isp_dev->setfile_lock);
++ return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+@@ -0,0 +1,222 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_ISP_H
++#define STF_ISP_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_ISP_NAME "stf_isp"
++#define STF_ISP_SETFILE "stf_isp0_fw.bin"
++
++#define ISP_SCD_BUFFER_SIZE (19 * 256 * 4) // align 128
++#define ISP_YHIST_BUFFER_SIZE (64 * 4)
++#define ISP_SCD_Y_BUFFER_SIZE (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
++#define ISP_RAW_DATA_BITS 12
++#define SCALER_RATIO_MAX 1 // no compose function
++#define STF_ISP_REG_OFFSET_MAX 0x0FFF
++#define STF_ISP_REG_DELAY_MAX 100
++
++#define ISP_REG_CSIINTS_ADDR 0x00000008
++#define ISP_REG_SENSOR 0x00000014
++#define ISP_REG_DUMP_CFG_0 0x00000024
++#define ISP_REG_DUMP_CFG_1 0x00000028
++#define ISP_REG_SCD_CFG_0 0x00000098
++#define ISP_REG_SCD_CFG_1 0x0000009C
++#define ISP_REG_SC_CFG_1 0x000000BC
++#define ISP_REG_IESHD_ADDR 0x00000A50
++#define ISP_REG_SS0AY 0x00000A94
++#define ISP_REG_SS0AUV 0x00000A98
++#define ISP_REG_SS0S 0x00000A9C
++#define ISP_REG_SS0IW 0x00000AA8
++#define ISP_REG_SS1AY 0x00000AAC
++#define ISP_REG_SS1AUV 0x00000AB0
++#define ISP_REG_SS1S 0x00000AB4
++#define ISP_REG_SS1IW 0x00000AC0
++#define ISP_REG_YHIST_CFG_4 0x00000CD8
++#define ISP_REG_ITIIWSR 0x00000B20
++#define ISP_REG_ITIDWLSR 0x00000B24
++#define ISP_REG_ITIDWYSAR 0x00000B28
++#define ISP_REG_ITIDWUSAR 0x00000B2C
++#define ISP_REG_ITIDRYSAR 0x00000B30
++#define ISP_REG_ITIDRUSAR 0x00000B34
++#define ISP_REG_ITIPDFR 0x00000B38
++#define ISP_REG_ITIDRLSR 0x00000B3C
++#define ISP_REG_ITIBSR 0x00000B40
++#define ISP_REG_ITIAIR 0x00000B44
++#define ISP_REG_ITIDPSR 0x00000B48
++
++/* The output line of a isp controller */
++enum isp_line_id {
++ STF_ISP_LINE_INVALID = -1,
++ STF_ISP_LINE_SRC = 1,
++ STF_ISP_LINE_SRC_SS0 = 2,
++ STF_ISP_LINE_SRC_SS1 = 3,
++ STF_ISP_LINE_SRC_ITIW = 4,
++ STF_ISP_LINE_SRC_ITIR = 5,
++ STF_ISP_LINE_SRC_RAW = 6,
++ STF_ISP_LINE_SRC_SCD_Y = 7,
++ STF_ISP_LINE_MAX = STF_ISP_LINE_SRC_SCD_Y
++};
++
++/* pad id for media framework */
++enum isp_pad_id {
++ STF_ISP_PAD_SINK = 0,
++ STF_ISP_PAD_SRC = 1,
++ STF_ISP_PAD_SRC_SS0 = 2,
++ STF_ISP_PAD_SRC_SS1 = 3,
++ STF_ISP_PAD_SRC_ITIW = 4,
++ STF_ISP_PAD_SRC_ITIR = 5,
++ STF_ISP_PAD_SRC_RAW = 6,
++ STF_ISP_PAD_SRC_SCD_Y = 7,
++ STF_ISP_PAD_MAX = 8
++};
++
++enum {
++ EN_INT_NONE = 0,
++ EN_INT_ISP_DONE = (0x1 << 24),
++ EN_INT_CSI_DONE = (0x1 << 25),
++ EN_INT_SC_DONE = (0x1 << 26),
++ EN_INT_LINE_INT = (0x1 << 27),
++ EN_INT_ALL = (0xF << 24),
++};
++
++enum {
++ DVP_SENSOR = 0,
++ CSI_SENSOR,
++};
++
++#define ISP_AWB_OECF_SKIP_FRAME 0
++// 0x0BC [31:30] SEL - sc0 input mux for sc awb
++// 00 : after DEC, 01 : after OBC, 10 : after OECF, 11 : after AWB
++enum scd_type {
++ DEC_TYPE = 0,
++ OBC_TYPE,
++ OECF_TYPE,
++ AWB_TYPE
++};
++
++struct isp_format {
++ u32 code;
++ u8 bpp;
++};
++
++struct isp_format_table {
++ const struct isp_format *fmts;
++ int nfmts;
++};
++
++struct regval_t {
++ u32 addr;
++ u32 val;
++ u32 mask;
++ u32 delay_ms;
++};
++
++struct reg_table {
++ struct regval_t *regval;
++ int regval_num;
++};
++
++struct isp_stream_format {
++ struct v4l2_rect rect;
++ u32 bpp;
++};
++
++struct stf_isp_dev;
++enum subdev_type;
++
++struct isp_hw_ops {
++ int (*isp_clk_enable)(struct stf_isp_dev *isp_dev);
++ int (*isp_clk_disable)(struct stf_isp_dev *isp_dev);
++ int (*isp_reset)(struct stf_isp_dev *isp_dev);
++ int (*isp_config_set)(struct stf_isp_dev *isp_dev);
++ int (*isp_set_format)(struct stf_isp_dev *isp_dev,
++ struct isp_stream_format *crop, u32 mcode,
++ int type);
++ // u32 width, u32 height);
++ int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on);
++ int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg);
++ int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg);
++ int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev);
++};
++
++struct isp_ctrls {
++ struct v4l2_ctrl_handler handler;
++ struct v4l2_ctrl *pixel_rate;
++ struct {
++ struct v4l2_ctrl *auto_exp;
++ struct v4l2_ctrl *exposure;
++ };
++ struct {
++ struct v4l2_ctrl *auto_wb;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *red_balance;
++ };
++ struct {
++ struct v4l2_ctrl *auto_gain;
++ struct v4l2_ctrl *gain;
++ };
++ struct v4l2_ctrl *brightness;
++ struct v4l2_ctrl *light_freq;
++ struct v4l2_ctrl *saturation;
++ struct v4l2_ctrl *contrast;
++ struct v4l2_ctrl *hue;
++ struct v4l2_ctrl *test_pattern;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vflip;
++};
++
++struct isp_setfile {
++ struct reg_table settings;
++ const u8 *data;
++ unsigned int size;
++ unsigned int state;
++};
++
++enum {
++ ISP_CROP = 0,
++ ISP_COMPOSE,
++ ISP_SCALE_SS0,
++ ISP_SCALE_SS1,
++ ISP_ITIWS,
++ ISP_RECT_MAX
++};
++
++struct stf_isp_dev {
++ enum subdev_type sdev_type; // must be frist
++ struct stfcamss *stfcamss;
++ struct v4l2_subdev subdev;
++ struct media_pad pads[STF_ISP_PAD_MAX];
++ struct v4l2_mbus_framefmt fmt[STF_ISP_PAD_MAX];
++ struct isp_stream_format rect[ISP_RECT_MAX];
++ const struct isp_format_table *formats;
++ unsigned int nformats;
++ struct isp_hw_ops *hw_ops;
++ struct mutex power_lock;
++ int power_count;
++ struct mutex stream_lock;
++ int stream_count;
++ atomic_t shadow_count;
++
++ struct isp_ctrls ctrls;
++ struct mutex setfile_lock;
++ struct isp_setfile setfile;
++ struct reg_table *context_regs;
++};
++
++extern int stf_isp_subdev_init(struct stfcamss *stfcamss);
++extern int stf_isp_register(struct stf_isp_dev *isp_dev,
++ struct v4l2_device *v4l2_dev);
++extern int stf_isp_unregister(struct stf_isp_dev *isp_dev);
++extern struct isp_hw_ops isp_ops;
++extern void dump_isp_reg(void *__iomem ispbase);
++
++#endif /* STF_ISP_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
+@@ -0,0 +1,1550 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/io.h>
++#include <linux/fb.h>
++#include <linux/module.h>
++#include <video/stf-vin.h>
++#include "stf_isp_ioctl.h"
++#include "stf_isp.h"
++#include <linux/delay.h>
++#include <linux/clk.h>
++#define USE_NEW_CONFIG_SETTING
++
++static struct regval_t isp_reg_init_config_list[] = {
++ /* config DC(0040H~0044H) */
++ {0x00000044, 0x00000000, 0, 0},
++ /* config DEC(0030H) */
++ {0x00000030, 0x00000000, 0, 0},
++ /* config OBC(0034H, 02E0H~02FCH) */
++ {0x00000034, 0x000000BB, 0, 0},
++ {0x000002E0, 0x40404040, 0, 0},
++ {0x000002E4, 0x40404040, 0, 0},
++ {0x000002E8, 0x40404040, 0, 0},
++ {0x000002EC, 0x40404040, 0, 0},
++ {0x000002F0, 0x00000000, 0, 0},
++ {0x000002F4, 0x00000000, 0, 0},
++ {0x000002F8, 0x00000000, 0, 0},
++ {0x000002FC, 0x00000000, 0, 0},
++ /* config LCBQ(0074H, 007CH, 0300H~039FH, and 0400H~049FH) */
++ {0x00000074, 0x00009900, 0, 0},
++ {0x0000007C, 0x01E40040, 0, 0},
++ {0x00000300, 0x01000100, 0, 0},
++ {0x00000304, 0x01000100, 0, 0},
++ {0x00000308, 0x01000100, 0, 0},
++ {0x0000030C, 0x01000100, 0, 0},
++ {0x00000310, 0x01000100, 0, 0},
++ {0x00000314, 0x01000100, 0, 0},
++ {0x00000318, 0x01000100, 0, 0},
++ {0x0000031C, 0x01000100, 0, 0},
++ {0x00000320, 0x01000100, 0, 0},
++ {0x00000324, 0x01000100, 0, 0},
++ {0x00000328, 0x01000100, 0, 0},
++ {0x0000032C, 0x01000100, 0, 0},
++ {0x00000330, 0x00000100, 0, 0},
++ {0x00000334, 0x01000100, 0, 0},
++ {0x00000338, 0x01000100, 0, 0},
++ {0x0000033C, 0x01000100, 0, 0},
++ {0x00000340, 0x01000100, 0, 0},
++ {0x00000344, 0x01000100, 0, 0},
++ {0x00000348, 0x01000100, 0, 0},
++ {0x0000034C, 0x01000100, 0, 0},
++ {0x00000350, 0x01000100, 0, 0},
++ {0x00000354, 0x01000100, 0, 0},
++ {0x00000358, 0x01000100, 0, 0},
++ {0x0000035C, 0x01000100, 0, 0},
++ {0x00000360, 0x01000100, 0, 0},
++ {0x00000364, 0x00000100, 0, 0},
++ {0x00000368, 0x01000100, 0, 0},
++ {0x0000036C, 0x01000100, 0, 0},
++ {0x00000370, 0x01000100, 0, 0},
++ {0x00000374, 0x01000100, 0, 0},
++ {0x00000378, 0x01000100, 0, 0},
++ {0x0000037C, 0x01000100, 0, 0},
++ {0x00000380, 0x01000100, 0, 0},
++ {0x00000384, 0x01000100, 0, 0},
++ {0x00000388, 0x01000100, 0, 0},
++ {0x0000038C, 0x01000100, 0, 0},
++ {0x00000390, 0x01000100, 0, 0},
++ {0x00000394, 0x01000100, 0, 0},
++ {0x00000398, 0x00000100, 0, 0},
++ {0x0000039C, 0x01000100, 0, 0},
++ {0x000003A0, 0x01000100, 0, 0},
++ {0x000003A4, 0x01000100, 0, 0},
++ {0x000003A8, 0x01000100, 0, 0},
++ {0x000003AC, 0x01000100, 0, 0},
++ {0x000003B0, 0x01000100, 0, 0},
++ {0x000003B4, 0x01000100, 0, 0},
++ {0x000003B8, 0x01000100, 0, 0},
++ {0x000003BC, 0x01000100, 0, 0},
++ {0x000003C0, 0x01000100, 0, 0},
++ {0x000003C4, 0x01000100, 0, 0},
++ {0x000003C8, 0x01000100, 0, 0},
++ {0x000003CC, 0x00000100, 0, 0},
++ {0x00000400, 0x00000000, 0, 0},
++ {0x00000404, 0x00000000, 0, 0},
++ {0x00000408, 0x00000000, 0, 0},
++ {0x0000040C, 0x00000000, 0, 0},
++ {0x00000410, 0x00000000, 0, 0},
++ {0x00000414, 0x00000000, 0, 0},
++ {0x00000418, 0x00000000, 0, 0},
++ {0x0000041C, 0x00000000, 0, 0},
++ {0x00000420, 0x00000000, 0, 0},
++ {0x00000424, 0x00000000, 0, 0},
++ {0x00000428, 0x00000000, 0, 0},
++ {0x0000042C, 0x00000000, 0, 0},
++ {0x00000430, 0x00000000, 0, 0},
++ {0x00000434, 0x00000000, 0, 0},
++ {0x00000438, 0x00000000, 0, 0},
++ {0x0000043C, 0x00000000, 0, 0},
++ {0x00000440, 0x00000000, 0, 0},
++ {0x00000444, 0x00000000, 0, 0},
++ {0x00000448, 0x00000000, 0, 0},
++ {0x0000044C, 0x00000000, 0, 0},
++ {0x00000450, 0x00000000, 0, 0},
++ {0x00000454, 0x00000000, 0, 0},
++ {0x00000458, 0x00000000, 0, 0},
++ {0x0000045C, 0x00000000, 0, 0},
++ {0x00000460, 0x00000000, 0, 0},
++ {0x00000464, 0x00000000, 0, 0},
++ {0x00000468, 0x00000000, 0, 0},
++ {0x0000046C, 0x00000000, 0, 0},
++ {0x00000470, 0x00000000, 0, 0},
++ {0x00000474, 0x00000000, 0, 0},
++ {0x00000478, 0x00000000, 0, 0},
++ {0x0000047C, 0x00000000, 0, 0},
++ {0x00000480, 0x00000000, 0, 0},
++ {0x00000484, 0x00000000, 0, 0},
++ {0x00000488, 0x00000000, 0, 0},
++ {0x0000048C, 0x00000000, 0, 0},
++ {0x00000490, 0x00000000, 0, 0},
++ {0x00000494, 0x00000000, 0, 0},
++ {0x00000498, 0x00000000, 0, 0},
++ {0x0000049C, 0x00000000, 0, 0},
++ {0x000004A0, 0x00000000, 0, 0},
++ {0x000004A4, 0x00000000, 0, 0},
++ {0x000004A8, 0x00000000, 0, 0},
++ {0x000004AC, 0x00000000, 0, 0},
++ {0x000004B0, 0x00000000, 0, 0},
++ {0x000004B4, 0x00000000, 0, 0},
++ {0x000004B8, 0x00000000, 0, 0},
++ {0x000004BC, 0x00000000, 0, 0},
++ {0x000004C0, 0x00000000, 0, 0},
++ {0x000004C4, 0x00000000, 0, 0},
++ {0x000004C8, 0x00000000, 0, 0},
++ {0x000004CC, 0x00000000, 0, 0},
++ /* config OECF(0100H~027CH) */
++ {0x00000100, 0x00100000, 0, 0},
++ {0x00000104, 0x00400020, 0, 0},
++ {0x00000108, 0x00800060, 0, 0},
++ {0x0000010C, 0x00C000A0, 0, 0},
++ {0x00000110, 0x010000E0, 0, 0},
++ {0x00000114, 0x02000180, 0, 0},
++ {0x00000118, 0x03000280, 0, 0},
++ {0x0000011C, 0x03FE0380, 0, 0},
++ {0x00000120, 0x00100000, 0, 0},
++ {0x00000124, 0x00400020, 0, 0},
++ {0x00000128, 0x00800060, 0, 0},
++ {0x0000012C, 0x00C000A0, 0, 0},
++ {0x00000130, 0x010000E0, 0, 0},
++ {0x00000134, 0x02000180, 0, 0},
++ {0x00000138, 0x03000280, 0, 0},
++ {0x0000013C, 0x03FE0380, 0, 0},
++ {0x00000140, 0x00100000, 0, 0},
++ {0x00000144, 0x00400020, 0, 0},
++ {0x00000148, 0x00800060, 0, 0},
++ {0x0000014C, 0x00C000A0, 0, 0},
++ {0x00000150, 0x010000E0, 0, 0},
++ {0x00000154, 0x02000180, 0, 0},
++ {0x00000158, 0x03000280, 0, 0},
++ {0x0000015C, 0x03FE0380, 0, 0},
++ {0x00000160, 0x00100000, 0, 0},
++ {0x00000164, 0x00400020, 0, 0},
++ {0x00000168, 0x00800060, 0, 0},
++ {0x0000016C, 0x00C000A0, 0, 0},
++ {0x00000170, 0x010000E0, 0, 0},
++ {0x00000174, 0x02000180, 0, 0},
++ {0x00000178, 0x03000280, 0, 0},
++ {0x0000017C, 0x03FE0380, 0, 0},
++ {0x00000180, 0x00100000, 0, 0},
++ {0x00000184, 0x00400020, 0, 0},
++ {0x00000188, 0x00800060, 0, 0},
++ {0x0000018C, 0x00C000A0, 0, 0},
++ {0x00000190, 0x010000E0, 0, 0},
++ {0x00000194, 0x02000180, 0, 0},
++ {0x00000198, 0x03000280, 0, 0},
++ {0x0000019C, 0x03FE0380, 0, 0},
++ {0x000001A0, 0x00100000, 0, 0},
++ {0x000001A4, 0x00400020, 0, 0},
++ {0x000001A8, 0x00800060, 0, 0},
++ {0x000001AC, 0x00C000A0, 0, 0},
++ {0x000001B0, 0x010000E0, 0, 0},
++ {0x000001B4, 0x02000180, 0, 0},
++ {0x000001B8, 0x03000280, 0, 0},
++ {0x000001BC, 0x03FE0380, 0, 0},
++ {0x000001C0, 0x00100000, 0, 0},
++ {0x000001C4, 0x00400020, 0, 0},
++ {0x000001C8, 0x00800060, 0, 0},
++ {0x000001CC, 0x00C000A0, 0, 0},
++ {0x000001D0, 0x010000E0, 0, 0},
++ {0x000001D4, 0x02000180, 0, 0},
++ {0x000001D8, 0x03000280, 0, 0},
++ {0x000001DC, 0x03FE0380, 0, 0},
++ {0x000001E0, 0x00100000, 0, 0},
++ {0x000001E4, 0x00400020, 0, 0},
++ {0x000001E8, 0x00800060, 0, 0},
++ {0x000001EC, 0x00C000A0, 0, 0},
++ {0x000001F0, 0x010000E0, 0, 0},
++ {0x000001F4, 0x02000180, 0, 0},
++ {0x000001F8, 0x03000280, 0, 0},
++ {0x000001FC, 0x03FE0380, 0, 0},
++ {0x00000200, 0x00800080, 0, 0},
++ {0x00000204, 0x00800080, 0, 0},
++ {0x00000208, 0x00800080, 0, 0},
++ {0x0000020C, 0x00800080, 0, 0},
++ {0x00000210, 0x00800080, 0, 0},
++ {0x00000214, 0x00800080, 0, 0},
++ {0x00000218, 0x00800080, 0, 0},
++ {0x0000021C, 0x00800080, 0, 0},
++ {0x00000220, 0x00800080, 0, 0},
++ {0x00000224, 0x00800080, 0, 0},
++ {0x00000228, 0x00800080, 0, 0},
++ {0x0000022C, 0x00800080, 0, 0},
++ {0x00000230, 0x00800080, 0, 0},
++ {0x00000234, 0x00800080, 0, 0},
++ {0x00000238, 0x00800080, 0, 0},
++ {0x0000023C, 0x00800080, 0, 0},
++ {0x00000240, 0x00800080, 0, 0},
++ {0x00000244, 0x00800080, 0, 0},
++ {0x00000248, 0x00800080, 0, 0},
++ {0x0000024C, 0x00800080, 0, 0},
++ {0x00000250, 0x00800080, 0, 0},
++ {0x00000254, 0x00800080, 0, 0},
++ {0x00000258, 0x00800080, 0, 0},
++ {0x0000025C, 0x00800080, 0, 0},
++ {0x00000260, 0x00800080, 0, 0},
++ {0x00000264, 0x00800080, 0, 0},
++ {0x00000268, 0x00800080, 0, 0},
++ {0x0000026C, 0x00800080, 0, 0},
++ {0x00000270, 0x00800080, 0, 0},
++ {0x00000274, 0x00800080, 0, 0},
++ {0x00000278, 0x00800080, 0, 0},
++ {0x0000027C, 0x00800080, 0, 0},
++ /* config OECFHM(03D0H~03E4H) */
++ {0x000003D0, 0x04000000, 0, 0},
++ {0x000003D4, 0x0C000800, 0, 0},
++ {0x000003D8, 0x00000FFF, 0, 0},
++ {0x000003DC, 0x08000800, 0, 0},
++ {0x000003E0, 0x08000800, 0, 0},
++ {0x000003E4, 0x00000800, 0, 0},
++ /* config LCCF(0050H, 0058H, 00E0H~00ECH) */
++ {0x00000050, 0x021C03C0, 0, 0},
++ {0x00000058, 0x0000000B, 0, 0},
++ {0x000000E0, 0x00000000, 0, 0},
++ {0x000000E4, 0x00000000, 0, 0},
++ {0x000000E8, 0x00000000, 0, 0},
++ {0x000000EC, 0x00000000, 0, 0},
++ /* config AWB(0280H~02DCH) */
++ {0x00000280, 0x00000000, 0, 0},
++ {0x00000284, 0x00000000, 0, 0},
++ {0x00000288, 0x00000000, 0, 0},
++ {0x0000028C, 0x00000000, 0, 0},
++ {0x00000290, 0x00000000, 0, 0},
++ {0x00000294, 0x00000000, 0, 0},
++ {0x00000298, 0x00000000, 0, 0},
++ {0x0000029C, 0x00000000, 0, 0},
++ {0x000002A0, 0x00000000, 0, 0},
++ {0x000002A4, 0x00000000, 0, 0},
++ {0x000002A8, 0x00000000, 0, 0},
++ {0x000002AC, 0x00000000, 0, 0},
++ {0x000002B0, 0x00000000, 0, 0},
++ {0x000002B4, 0x00000000, 0, 0},
++ {0x000002B8, 0x00000000, 0, 0},
++ {0x000002BC, 0x00000000, 0, 0},
++ {0x000002C0, 0x00800080, 0, 0},
++ {0x000002C4, 0x00800080, 0, 0},
++ {0x000002C8, 0x00800080, 0, 0},
++ {0x000002CC, 0x00800080, 0, 0},
++ {0x000002D0, 0x00800080, 0, 0},
++ {0x000002D4, 0x00800080, 0, 0},
++ {0x000002D8, 0x00800080, 0, 0},
++ {0x000002DC, 0x00800080, 0, 0},
++ /* config CTC(0A10H) and DBC(0A14H) filter */
++ {0x00000A10, 0x41400040, 0, 0},
++ {0x00000A14, 0x02000200, 0, 0},
++ /* config CFA(0018H, 0A1CH) */
++ {0x00000018, 0x000011BB, 0, 0},
++ {0x00000A1C, 0x00000032, 0, 0},
++ /* config CCM(0C40H~0CA4H) */
++ {0x00000C40, 0x00060000, 0, 0},
++ {0x00000C44, 0x00000000, 0, 0},
++ {0x00000C48, 0x00000000, 0, 0},
++ {0x00000C4C, 0x00000000, 0, 0},
++ {0x00000C50, 0x00000000, 0, 0},
++ {0x00000C54, 0x00000000, 0, 0},
++ {0x00000C58, 0x00000000, 0, 0},
++ {0x00000C5C, 0x00000000, 0, 0},
++ {0x00000C60, 0x00000000, 0, 0},
++ {0x00000C64, 0x00000000, 0, 0},
++ {0x00000C68, 0x00000000, 0, 0},
++ {0x00000C6C, 0x00000000, 0, 0},
++ {0x00000C70, 0x00000080, 0, 0},
++ {0x00000C74, 0x00000000, 0, 0},
++ {0x00000C78, 0x00000000, 0, 0},
++ {0x00000C7C, 0x00000000, 0, 0},
++ {0x00000C80, 0x00000080, 0, 0},
++ {0x00000C84, 0x00000000, 0, 0},
++ {0x00000C88, 0x00000000, 0, 0},
++ {0x00000C8C, 0x00000000, 0, 0},
++ {0x00000C90, 0x00000080, 0, 0},
++ {0x00000C94, 0x00000000, 0, 0},
++ {0x00000C98, 0x00000000, 0, 0},
++ {0x00000C9C, 0x00000000, 0, 0},
++ {0x00000CA0, 0x00000700, 0, 0},
++ {0x00000CA4, 0x00000200, 0, 0},
++ /* config GMARGB(0E00H~0E38H) */
++ {0x00000E00, 0x24000000, 0, 0},
++ {0x00000E04, 0x08000020, 0, 0},
++ {0x00000E08, 0x08000040, 0, 0},
++ {0x00000E0C, 0x08000060, 0, 0},
++ {0x00000E10, 0x08000080, 0, 0},
++ {0x00000E14, 0x080000A0, 0, 0},
++ {0x00000E18, 0x080000C0, 0, 0},
++ {0x00000E1C, 0x080000E0, 0, 0},
++ {0x00000E20, 0x08000100, 0, 0},
++ {0x00000E24, 0x08000180, 0, 0},
++ {0x00000E28, 0x08000200, 0, 0},
++ {0x00000E2C, 0x08000280, 0, 0},
++ {0x00000E30, 0x08000300, 0, 0},
++ {0x00000E34, 0x08000380, 0, 0},
++ {0x00000E38, 0x080003FE, 0, 0},
++ /* config R2Y(0E40H~0E60H) */
++ {0x00000E40, 0x0000004C, 0, 0},
++ {0x00000E44, 0x00000097, 0, 0},
++ {0x00000E48, 0x0000001D, 0, 0},
++ {0x00000E4C, 0x000001D5, 0, 0},
++ {0x00000E50, 0x000001AC, 0, 0},
++ {0x00000E54, 0x00000080, 0, 0},
++ {0x00000E58, 0x00000080, 0, 0},
++ {0x00000E5C, 0x00000194, 0, 0},
++ {0x00000E60, 0x000001EC, 0, 0},
++ /* config YCRV(0F00H~0FFCH) */
++ {0x00000F00, 0x00000000, 0, 0},
++ {0x00000F04, 0x00000010, 0, 0},
++ {0x00000F08, 0x00000020, 0, 0},
++ {0x00000F0C, 0x00000030, 0, 0},
++ {0x00000F10, 0x00000040, 0, 0},
++ {0x00000F14, 0x00000050, 0, 0},
++ {0x00000F18, 0x00000060, 0, 0},
++ {0x00000F1C, 0x00000070, 0, 0},
++ {0x00000F20, 0x00000080, 0, 0},
++ {0x00000F24, 0x00000090, 0, 0},
++ {0x00000F28, 0x000000A0, 0, 0},
++ {0x00000F2C, 0x000000B0, 0, 0},
++ {0x00000F30, 0x000000C0, 0, 0},
++ {0x00000F34, 0x000000D0, 0, 0},
++ {0x00000F38, 0x000000E0, 0, 0},
++ {0x00000F3C, 0x000000F0, 0, 0},
++ {0x00000F40, 0x00000100, 0, 0},
++ {0x00000F44, 0x00000110, 0, 0},
++ {0x00000F48, 0x00000120, 0, 0},
++ {0x00000F4C, 0x00000130, 0, 0},
++ {0x00000F50, 0x00000140, 0, 0},
++ {0x00000F54, 0x00000150, 0, 0},
++ {0x00000F58, 0x00000160, 0, 0},
++ {0x00000F5C, 0x00000170, 0, 0},
++ {0x00000F60, 0x00000180, 0, 0},
++ {0x00000F64, 0x00000190, 0, 0},
++ {0x00000F68, 0x000001A0, 0, 0},
++ {0x00000F6C, 0x000001B0, 0, 0},
++ {0x00000F70, 0x000001C0, 0, 0},
++ {0x00000F74, 0x000001D0, 0, 0},
++ {0x00000F78, 0x000001E0, 0, 0},
++ {0x00000F7C, 0x000001F0, 0, 0},
++ {0x00000F80, 0x00000200, 0, 0},
++ {0x00000F84, 0x00000210, 0, 0},
++ {0x00000F88, 0x00000220, 0, 0},
++ {0x00000F8C, 0x00000230, 0, 0},
++ {0x00000F90, 0x00000240, 0, 0},
++ {0x00000F94, 0x00000250, 0, 0},
++ {0x00000F98, 0x00000260, 0, 0},
++ {0x00000F9C, 0x00000270, 0, 0},
++ {0x00000FA0, 0x00000280, 0, 0},
++ {0x00000FA4, 0x00000290, 0, 0},
++ {0x00000FA8, 0x000002A0, 0, 0},
++ {0x00000FAC, 0x000002B0, 0, 0},
++ {0x00000FB0, 0x000002C0, 0, 0},
++ {0x00000FB4, 0x000002D0, 0, 0},
++ {0x00000FB8, 0x000002E0, 0, 0},
++ {0x00000FBC, 0x000002F0, 0, 0},
++ {0x00000FC0, 0x00000300, 0, 0},
++ {0x00000FC4, 0x00000310, 0, 0},
++ {0x00000FC8, 0x00000320, 0, 0},
++ {0x00000FCC, 0x00000330, 0, 0},
++ {0x00000FD0, 0x00000340, 0, 0},
++ {0x00000FD4, 0x00000350, 0, 0},
++ {0x00000FD8, 0x00000360, 0, 0},
++ {0x00000FDC, 0x00000370, 0, 0},
++ {0x00000FE0, 0x00000380, 0, 0},
++ {0x00000FE4, 0x00000390, 0, 0},
++ {0x00000FE8, 0x000003A0, 0, 0},
++ {0x00000FEC, 0x000003B0, 0, 0},
++ {0x00000FF0, 0x000003C0, 0, 0},
++ {0x00000FF4, 0x000003D0, 0, 0},
++ {0x00000FF8, 0x000003E0, 0, 0},
++ {0x00000FFC, 0x000003F0, 0, 0},
++ /* config Shrp(0E80H~0EE8H) */
++ {0x00000E80, 0x00070F00, 0, 0},
++ {0x00000E84, 0x00180F00, 0, 0},
++ {0x00000E88, 0x00800F00, 0, 0},
++ {0x00000E8C, 0x01000F00, 0, 0},
++ {0x00000E90, 0x00100F00, 0, 0},
++ {0x00000E94, 0x00600F00, 0, 0},
++ {0x00000E98, 0x01000F00, 0, 0},
++ {0x00000E9C, 0x01900F00, 0, 0},
++ {0x00000EA0, 0x00000F00, 0, 0},
++ {0x00000EA4, 0x00000F00, 0, 0},
++ {0x00000EA8, 0x00000F00, 0, 0},
++ {0x00000EAC, 0x00000F00, 0, 0},
++ {0x00000EB0, 0x00000F00, 0, 0},
++ {0x00000EB4, 0x00000F00, 0, 0},
++ {0x00000EB8, 0x00000F00, 0, 0},
++ {0x00000EBC, 0x10000000, 0, 0},
++ {0x00000EC0, 0x10000000, 0, 0},
++ {0x00000EC4, 0x10000000, 0, 0},
++ {0x00000EC8, 0x10000000, 0, 0},
++ {0x00000ECC, 0x10000000, 0, 0},
++ {0x00000ED0, 0x10000000, 0, 0},
++ {0x00000ED4, 0x88000D7C, 0, 0},
++ {0x00000ED8, 0x00C00040, 0, 0},
++ {0x00000EDC, 0xFF000000, 0, 0},
++ {0x00000EE0, 0x00A00040, 0, 0},
++ {0x00000EE4, 0x00000000, 0, 0},
++ {0x00000EE8, 0x00000000, 0, 0},
++ /* config DNYUV(0C00H~0C24H) */
++ {0x00000C00, 0x00777777, 0, 0},
++ {0x00000C04, 0x00007777, 0, 0},
++ {0x00000C08, 0x00777777, 0, 0},
++ {0x00000C0C, 0x00007777, 0, 0},
++ {0x00000C10, 0x00600040, 0, 0},
++ {0x00000C14, 0x00D80090, 0, 0},
++ {0x00000C18, 0x01E60144, 0, 0},
++ {0x00000C1C, 0x00600040, 0, 0},
++ {0x00000C20, 0x00D80090, 0, 0},
++ {0x00000C24, 0x01E60144, 0, 0},
++ /* config SAT(0A30H~0A40H, 0A54H~0A58H) */
++ {0x00000A30, 0x00000100, 0, 0},
++ {0x00000A34, 0x001F0001, 0, 0},
++ {0x00000A38, 0x00000000, 0, 0},
++ {0x00000A3C, 0x00000100, 0, 0},
++ {0x00000A40, 0x00000008, 0, 0},
++ {0x00000A54, 0x04010001, 0, 0},
++ {0x00000A58, 0x03FF0001, 0, 0},
++ /* config OBA(0090H~0094H) */
++ {0x00000090, 0x04380000, 0, 0},
++ {0x00000094, 0x04390780, 0, 0},
++ /* config SC(0098H~009CH, 00B8H~00BCH,
++ * 00C0H, 0C4H~0D4H, 04D0H~054CH, 5D0H~5D4H)
++ */
++ {0x0000009C, 0x01000000, 0, 0},
++ {0x000000B8, 0x000C0000, 0, 0},
++ {0x000000BC, 0xC010151D, 0, 0},
++ {0x000000C0, 0x01F1BF08, 0, 0},
++ {0x000000C4, 0xFF00FF00, 0, 0},
++ {0x000000C8, 0xFF00FF00, 0, 0},
++ {0x000000CC, 0xFFFF0000, 0, 0},
++ {0x000000D0, 0xFFFF0000, 0, 0},
++ {0x000000D4, 0xFFFF0000, 0, 0},
++ {0x000000D8, 0x01050107, 0, 0},
++ {0x000004D0, 0x00000000, 0, 0},
++ {0x000004D4, 0x00000000, 0, 0},
++ {0x000004D8, 0x00000000, 0, 0},
++ {0x000004DC, 0x00000000, 0, 0},
++ {0x000004E0, 0x00000000, 0, 0},
++ {0x000004E4, 0x00000000, 0, 0},
++ {0x000004E8, 0x00000000, 0, 0},
++ {0x000004EC, 0x00000000, 0, 0},
++ {0x000004F0, 0x00100000, 0, 0},
++ {0x000004F4, 0x00000000, 0, 0},
++ {0x000004F8, 0x03D20000, 0, 0},
++ {0x000004FC, 0x00000000, 0, 0},
++ {0x00000500, 0x00950000, 0, 0},
++ {0x00000504, 0x00000000, 0, 0},
++ {0x00000508, 0x00253000, 0, 0},
++ {0x0000050C, 0x00000000, 0, 0},
++ {0x00000510, 0x00000000, 0, 0},
++ {0x00000514, 0x00000000, 0, 0},
++ {0x00000518, 0x00000000, 0, 0},
++ {0x0000051C, 0x00000000, 0, 0},
++ {0x00000520, 0x00000000, 0, 0},
++ {0x00000524, 0x00000000, 0, 0},
++ {0x00000528, 0x00000000, 0, 0},
++ {0x0000052C, 0x00000000, 0, 0},
++ {0x00000530, 0x00000000, 0, 0},
++ {0x00000534, 0x00000000, 0, 0},
++ {0x00000538, 0xFFFFFFF0, 0, 0},
++ {0x0000053C, 0x8FFFFFFF, 0, 0},
++ {0x00000540, 0x0000001E, 0, 0},
++ {0x00000544, 0x00000000, 0, 0},
++ {0x00000548, 0x00000000, 0, 0},
++ {0x0000054C, 0xF0F20000, 0, 0},
++ {0x000005D0, 0xFF00FF00, 0, 0},
++ {0x000005D4, 0xFF00FF00, 0, 0},
++ /* config YHIST(0CC8H~0CD8H) */
++ {0x00000CC8, 0x00000000, 0, 0},
++ {0x00000CCC, 0x0437077F, 0, 0},
++ {0x00000CD0, 0x00010002, 0, 0},
++ {0x00000CD4, 0x00000000, 0, 0},
++ /* config CBAR(0600H-0653H) */
++ {0x00000600, 0x043E0782, 0, 0},
++ {0x00000604, 0x00000000, 0, 0},
++ {0x00000608, 0x0437077F, 0, 0},
++ {0x0000060C, 0x00443150, 0, 0},
++ {0x00000610, 0x00000000, 0, 0},
++ {0x00000614, 0x08880888, 0, 0},
++ {0x00000618, 0x02220222, 0, 0},
++ {0x0000061C, 0x04440444, 0, 0},
++ {0x00000620, 0x08880888, 0, 0},
++ {0x00000624, 0x0AAA0AAA, 0, 0},
++ {0x00000628, 0x0CCC0CCC, 0, 0},
++ {0x0000062C, 0x0EEE0EEE, 0, 0},
++ {0x00000630, 0x0FFF0FFF, 0, 0},
++ {0x00000634, 0x08880888, 0, 0},
++ {0x00000638, 0x02220222, 0, 0},
++ {0x0000063C, 0x04440444, 0, 0},
++ {0x00000640, 0x08880888, 0, 0},
++ {0x00000644, 0x0AAA0AAA, 0, 0},
++ {0x00000648, 0x0CCC0CCC, 0, 0},
++ {0x0000064C, 0x0EEE0EEE, 0, 0},
++ {0x00000650, 0x0FFF0FFF, 0, 0},
++ /* config sensor(0014H) */
++ {0x00000014, 0x0000000c, 0, 0},
++ /* config CROP(001CH, 0020H) */
++ {0x0000001C, 0x00000000, 0, 0},
++ {0x00000020, 0x0437077F, 0, 0},
++ /* config isp pileline X/Y size(A0CH) */
++ {0x00000A0C, 0x04380780, 0, 0},
++ /* config CSI dump (24H/28H) */
++ {0x00000028, 0x00030B80, 0, 0},
++ /* Video Output */
++ /* config UO(0A80H~0A90H) */
++ {0x00000A88, 0x00000780, 0, 0},
++ /* NV12 */
++ {0x00000A8C, 0x00000000, 0, 0},
++ /* NV21
++ *{0x00000A8C, 0x00000020, 0, 0},
++ */
++ {0x00000A90, 0x00000000, 0, 0},
++ {0x00000A9C, 0x00000780, 0, 0},
++ {0x00000AA0, 0x00000002, 0, 0},
++ {0x00000AA4, 0x00000002, 0, 0},
++ {0x00000AA8, 0x07800438, 0, 0},
++ {0x00000AB4, 0x00000780, 0, 0},
++ {0x00000AB8, 0x00000002, 0, 0},
++ {0x00000ABC, 0x00000002, 0, 0},
++ {0x00000AC0, 0x07800438, 0, 0},
++ {0x00000AC4, 0x00000000, 0, 0},
++ /* config TIL(0B20H~0B48H) */
++ {0x00000B20, 0x04380780, 0, 0},
++ {0x00000B24, 0x00000960, 0, 0},
++ {0x00000B38, 0x00030003, 0, 0},
++ {0x00000B3C, 0x00000960, 0, 0},
++ {0x00000B44, 0x00000000, 0, 0},
++ {0x00000B48, 0x00000000, 0, 0},
++ /* Enable DEC/OBC/OECF/LCCF/AWB/SC/DUMP */
++ {0x00000010, 0x000A00D6, 0x00000000, 0x00},
++ /* Enable CFA/CAR/CCM/GMARGB/R2Y/SHRP/SAT/DNYUV/YCRV/YHIST/CTC/DBC */
++ {0x00000A08, 0x107A01BE, 0x00000000, 0x00},
++};
++
++const struct reg_table isp_reg_init_settings[] = {
++ {isp_reg_init_config_list,
++ ARRAY_SIZE(isp_reg_init_config_list)},
++};
++
++static struct regval_t isp_reg_start_config_list[] = {
++#if defined(ENABLE_SS0_SS1)
++ /* ENABLE UO/SS0/SS1/Multi-Frame and Reset ISP */
++ {0x00000A00, 0x00121802, 0x00000000, 0x0A},
++ /* ENABLE UO/SS0/SS1/Multi-Frame and Leave ISP reset */
++ {0x00000A00, 0x00121800, 0x00000000, 0x0A},
++#else
++ /* ENABLE UO/Multi-Frame and Reset ISP */
++ {0x00000A00, 0x00120002, 0x00000000, 0x0A},
++ /* ENABLE UO/Multi-Frame and Leave ISP reset */
++ {0x00000A00, 0x00120000, 0x00000000, 0x0A},
++#endif
++ /* Config ISP shadow mode as next-vsync */
++ {0x00000A50, 0x00000002, 0x00000000, 0x00},
++#if defined(ENABLE_SS0_SS1)
++ /* ENABLE UO/SS0/SS1/Multi-Frame and Enable ISP */
++ {0x00000A00, 0x00121801, 0x00000000, 0x0A},
++#else
++ /* ENABLE UO/Multi-Frame and Enable ISP */
++ {0x00000A00, 0x00120001, 0x00000000, 0x0A},
++#endif
++ /* Config CSI shadow mode as immediate to fetch current setting */
++ {0x00000008, 0x00010004, 0x00000000, 0x0A},
++ /* Config CSI shadow mode as next-vsync */
++ {0x00000008, 0x00020004, 0x00000000, 0x00},
++ /* Enable CSI */
++ {0x00000000, 0x00000001, 0x00000000, 0x0A},
++};
++
++const struct reg_table isp_reg_start_settings[] = {
++ {isp_reg_start_config_list,
++ ARRAY_SIZE(isp_reg_start_config_list)},
++};
++
++static struct regval_t isp_imx_219_reg_config_list[] = {
++ /* MIPI sensor */
++ {0x00000014, 0x0000000D, 0, 0},
++ /* config CFA(0018H, 0A1CH) */
++ {0x00000A1C, 0x00000032, 0, 0},
++ {0x00000A8C, 0x00000000, 0, 0},
++ {0x00000A90, 0x00000000, 0, 0},
++ /* config R2Y(0E40H~0E60H) */
++ {0x00000E40, 0x0000004C, 0, 0},
++ {0x00000E44, 0x00000097, 0, 0},
++ {0x00000E48, 0x0000001D, 0, 0},
++ {0x00000E4C, 0x000001D5, 0, 0},
++ {0x00000E50, 0x000001AC, 0, 0},
++ {0x00000E54, 0x00000080, 0, 0},
++ {0x00000E58, 0x00000080, 0, 0},
++ {0x00000E5C, 0x00000194, 0, 0},
++ {0x00000E60, 0x000001EC, 0, 0},
++ /* Config AWB(0280H~02DCH). Fixed WB gain for IMX-219 sensor. */
++ {0x00000280, 0x00000000, 0, 0},
++ {0x00000284, 0x00000000, 0, 0},
++ {0x00000288, 0x00000000, 0, 0},
++ {0x0000028C, 0x00000000, 0, 0},
++ {0x00000290, 0x00000000, 0, 0},
++ {0x00000294, 0x00000000, 0, 0},
++ {0x00000298, 0x00000000, 0, 0},
++ {0x0000029C, 0x00000000, 0, 0},
++ {0x000002A0, 0x00000000, 0, 0},
++ {0x000002A4, 0x00000000, 0, 0},
++ {0x000002A8, 0x00000000, 0, 0},
++ {0x000002AC, 0x00000000, 0, 0},
++ {0x000002B0, 0x00000000, 0, 0},
++ {0x000002B4, 0x00000000, 0, 0},
++ {0x000002B8, 0x00000000, 0, 0},
++ {0x000002BC, 0x00000000, 0, 0},
++ {0x000002C0, 0x00F000F0, 0, 0},
++ {0x000002C4, 0x00F000F0, 0, 0},
++ {0x000002C8, 0x00800080, 0, 0},
++ {0x000002CC, 0x00800080, 0, 0},
++ {0x000002D0, 0x00800080, 0, 0},
++ {0x000002D4, 0x00800080, 0, 0},
++ {0x000002D8, 0x00B000B0, 0, 0},
++ {0x000002DC, 0x00B000B0, 0, 0},
++ /* config GMARGB(0E00H~0E38H)
++ * Gamma RGB 1.9 for IMX-219 sensor
++ */
++ {0x00000E00, 0x24000000, 0, 0},
++ {0x00000E04, 0x159500A5, 0, 0},
++ {0x00000E08, 0x0F9900EE, 0, 0},
++ {0x00000E0C, 0x0CE40127, 0, 0},
++ {0x00000E10, 0x0B410157, 0, 0},
++ {0x00000E14, 0x0A210181, 0, 0},
++ {0x00000E18, 0x094B01A8, 0, 0},
++ {0x00000E1C, 0x08A401CC, 0, 0},
++ {0x00000E20, 0x081D01EE, 0, 0},
++ {0x00000E24, 0x06B20263, 0, 0},
++ {0x00000E28, 0x05D802C7, 0, 0},
++ {0x00000E2C, 0x05420320, 0, 0},
++ {0x00000E30, 0x04D30370, 0, 0},
++ {0x00000E34, 0x047C03BB, 0, 0},
++ {0x00000E38, 0x043703FF, 0, 0},
++ {0x00000010, 0x00000080, 0, 0},
++ /* Enable CFA/GMARGB/R2Y */
++ {0x00000A08, 0x10000032, 0x0FFFFFFF, 0x00},
++ {0x00000A00, 0x00120002, 0, 0},
++ {0x00000A00, 0x00120000, 0, 0},
++ {0x00000A50, 0x00000002, 0, 0},
++ {0x00000008, 0x00010000, 0, 0},
++ {0x00000008, 0x0002000A, 0, 0},
++ {0x00000000, 0x00000001, 0, 0},
++};
++
++const struct reg_table isp_imx_219_settings[] = {
++ {isp_imx_219_reg_config_list,
++ ARRAY_SIZE(isp_imx_219_reg_config_list)},
++};
++
++static struct regval_t isp_format_reg_list[] = {
++ {0x0000001C, 0x00000000, 0x00000000, 0},
++ {0x00000020, 0x0437077F, 0x00000000, 0},
++ {0x00000A0C, 0x04380780, 0x00000000, 0},
++ {0x00000A88, 0x00000780, 0x00000000, 0},
++ {0x00000018, 0x000011BB, 0x00000000, 0},
++ {0x00000A08, 0x10000000, 0xF0000000, 0},
++ {0x00000028, 0x00030B80, 0x0003FFFF, 0},
++ {0x00000AA8, 0x07800438, 0x00000000, 0},
++ {0x00000A9C, 0x00000780, 0x00000000, 0},
++ {0x00000AC0, 0x07800438, 0x00000000, 0},
++ {0x00000AB4, 0x00000780, 0x00000000, 0},
++ {0x00000B20, 0x04380780, 0x00000000, 0},
++ {0x00000B24, 0x00000960, 0x00000000, 0},
++ {0x00000B3C, 0x00000960, 0x00000000, 0},
++ {0x00000014, 0x00000008, 0x00000000, 0},
++};
++
++const struct reg_table isp_format_settings[] = {
++ {isp_format_reg_list,
++ ARRAY_SIZE(isp_format_reg_list)},
++};
++
++#if defined(USE_NEW_CONFIG_SETTING)
++#else
++static struct reg_table *isp_settings = (struct reg_table *)isp_imx_219_settings;
++#endif
++
++static void isp_load_regs(void __iomem *ispbase, const struct reg_table *table)
++{
++ int j;
++ u32 delay_ms, reg_addr, mask, val;
++
++ for (j = 0; j < table->regval_num; j++) {
++ delay_ms = table->regval[j].delay_ms;
++ reg_addr = table->regval[j].addr;
++ val = table->regval[j].val;
++ mask = table->regval[j].mask;
++
++ if (reg_addr % 4
++ || reg_addr > STF_ISP_REG_OFFSET_MAX
++ || delay_ms > STF_ISP_REG_DELAY_MAX)
++ continue;
++
++ if (mask)
++ reg_set_bit(ispbase, reg_addr, mask, val);
++ else
++ reg_write(ispbase, reg_addr, val);
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++}
++
++static void isp_load_regs_exclude_csi_isp_enable(
++ void __iomem *ispbase,
++ const struct reg_table *table)
++{
++ int j;
++ u32 delay_ms, reg_addr, mask, val;
++
++ for (j = 0; j < table->regval_num; j++) {
++ delay_ms = table->regval[j].delay_ms;
++ reg_addr = table->regval[j].addr;
++ val = table->regval[j].val;
++ mask = table->regval[j].mask;
++
++ if (reg_addr % 4
++ || reg_addr > STF_ISP_REG_OFFSET_MAX
++ || delay_ms > STF_ISP_REG_DELAY_MAX
++ || ((reg_addr == ISP_REG_CSI_INPUT_EN_AND_STATUS) && (val & 0x01))
++ || ((reg_addr == ISP_REG_ISP_CTRL_0) && (val & 0x01)))
++ continue;
++
++ if (mask)
++ reg_set_bit(ispbase, reg_addr, mask, val);
++ else
++ reg_write(ispbase, reg_addr, val);
++ if (delay_ms)
++ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++ }
++}
++
++static int stf_isp_clk_enable(struct stf_isp_dev *isp_dev)
++{
++ struct stfcamss *stfcamss = isp_dev->stfcamss;
++
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_WRAPPER_C].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_WRAPPER_P].rstc);
++
++ return 0;
++}
++
++static int stf_isp_clk_disable(struct stf_isp_dev *isp_dev)
++{
++ struct stfcamss *stfcamss = isp_dev->stfcamss;
++
++ reset_control_assert(stfcamss->sys_rst[STFRST_WRAPPER_C].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_WRAPPER_P].rstc);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk);
++
++ return 0;
++}
++
++static void __iomem *stf_isp_get_ispbase(struct stf_vin_dev *vin)
++{
++ void __iomem *base = vin->isp_base;
++
++ return base;
++}
++
++static int stf_isp_save_ctx_regs(struct stf_isp_dev *isp_dev)
++{
++ int j;
++ u32 addr, val;
++ void __iomem *ispbase;
++ struct device *dev = isp_dev->stfcamss->dev;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ if (!isp_dev->context_regs) {
++ int regs_size =
++ sizeof(struct regval_t) * isp_reg_init_settings->regval_num;
++ isp_dev->context_regs =
++ devm_kzalloc(dev, sizeof(struct reg_table), GFP_KERNEL);
++ isp_dev->context_regs->regval =
++ devm_kzalloc(dev, regs_size, GFP_KERNEL);
++ isp_dev->context_regs->regval_num = isp_reg_init_settings->regval_num;
++ }
++
++ if (!isp_dev->context_regs || !isp_dev->context_regs->regval)
++ return -ENOMEM;
++
++ st_debug(ST_ISP, "Saving ISP context registers\n");
++ for (j = 0; j < isp_reg_init_settings->regval_num; j++) {
++ addr = isp_reg_init_settings->regval[j].addr;
++ val = ioread32(ispbase + addr);
++ isp_dev->context_regs->regval[j].addr = addr;
++ isp_dev->context_regs->regval[j].val = val;
++ }
++ st_debug(ST_ISP, "ISP context registers have been saved\n");
++
++ return 0;
++};
++
++static int stf_isp_restore_ctx_regs(struct stf_isp_dev *isp_dev)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ if (isp_dev->context_regs) {
++ isp_load_regs(ispbase, isp_dev->context_regs);
++ st_debug(ST_ISP, "Restored ISP register: isp_reg_init_settings.\n");
++ }
++
++ return 0;
++}
++
++static int stf_isp_reset(struct stf_isp_dev *isp_dev)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), BIT(1));
++ reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), 0);
++
++ return 0;
++}
++
++static int stf_isp_config_set(struct stf_isp_dev *isp_dev)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ st_debug(ST_ISP, "%s\n", __func__);
++
++#if defined(USE_NEW_CONFIG_SETTING)
++ mutex_lock(&isp_dev->setfile_lock);
++
++ if (isp_dev->context_regs) {
++ stf_isp_restore_ctx_regs(isp_dev);
++ st_debug(ST_ISP, "%s context regs restore done\n", __func__);
++ } else {
++ isp_load_regs(ispbase, isp_reg_init_settings);
++ st_debug(ST_ISP, "%s isp_reg_init_settings done\n", __func__);
++ }
++ if (isp_dev->setfile.state) {
++ st_info(ST_ISP, "%s, Program extra ISP setting!\n", __func__);
++ isp_load_regs_exclude_csi_isp_enable(ispbase,
++ &isp_dev->setfile.settings);
++ }
++
++ mutex_unlock(&isp_dev->setfile_lock);
++#else
++ mutex_lock(&isp_dev->setfile_lock);
++ if (isp_dev->setfile.state)
++ isp_load_regs(ispbase, &isp_dev->setfile.settings);
++ else
++ isp_load_regs(ispbase, isp_settings);
++ mutex_unlock(&isp_dev->setfile_lock);
++
++ st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++ isp_format_reg_list[0].addr,
++ isp_format_reg_list[0].val);
++ st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++ isp_format_reg_list[1].addr,
++ isp_format_reg_list[1].val);
++ st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++ isp_format_reg_list[2].addr,
++ isp_format_reg_list[2].val);
++ st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++ isp_format_reg_list[3].addr,
++ isp_format_reg_list[3].val);
++#endif
++
++ return 0;
++}
++
++static int stf_isp_set_format(struct stf_isp_dev *isp_dev,
++ struct isp_stream_format *crop_array, u32 mcode,
++ int type)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ struct stf_dvp_dev *dvp_dev = isp_dev->stfcamss->dvp_dev;
++ struct v4l2_rect *crop = &crop_array[ISP_COMPOSE].rect;
++ u32 bpp = crop_array[ISP_COMPOSE].bpp;
++ void __iomem *ispbase;
++ u32 val, val1;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ st_debug(ST_ISP, "interface type is %d(%s)\n",
++ type, type == CSI_SENSOR ? "CSI" : "DVP");
++
++ if (type == DVP_SENSOR) {
++ unsigned int flags = dvp_dev->dvp->flags;
++
++ st_debug(ST_ISP, "dvp flags = 0x%x, hsync active is %s, vsync active is %s\n",
++ flags, flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ? "high" : "low",
++ flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ? "high" : "low");
++ }
++
++ val = crop->left + (crop->top << 16);
++ isp_format_reg_list[0].addr = ISP_REG_PIC_CAPTURE_START_CFG;
++ isp_format_reg_list[0].val = val;
++
++ val = (crop->width + crop->left - 1)
++ + ((crop->height + crop->top - 1) << 16);
++ isp_format_reg_list[1].addr = ISP_REG_PIC_CAPTURE_END_CFG;
++ isp_format_reg_list[1].val = val;
++
++ val = crop->width + (crop->height << 16);
++ isp_format_reg_list[2].addr = ISP_REG_PIPELINE_XY_SIZE;
++ isp_format_reg_list[2].val = val;
++
++ isp_format_reg_list[3].addr = ISP_REG_STRIDE;
++ isp_format_reg_list[3].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++ switch (mcode) {
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ // 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R
++ val = 0x0000EE44;
++ val1 = 0x00000000;
++ break;
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ // 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr
++ val = 0x0000BB11;
++ val1 = 0x20000000;
++ break;
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ // 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb
++ val = 0x000044EE;
++ val1 = 0x30000000;
++ break;
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ // 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B
++ val = 0x000011BB;
++ val1 = 0x10000000;
++ break;
++ default:
++ st_err(ST_ISP, "UNKNOW format\n");
++ val = 0x000011BB;
++ val1 = 0x10000000;
++ break;
++ }
++
++ isp_format_reg_list[4].addr = ISP_REG_RAW_FORMAT_CFG;
++ isp_format_reg_list[4].val = val;
++
++ isp_format_reg_list[5].addr = ISP_REG_ISP_CTRL_1;
++ isp_format_reg_list[5].val = val1;
++ isp_format_reg_list[5].mask = 0xF0000000;
++
++ st_info(ST_ISP, "src left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++ crop->left, crop->top, crop->width, crop->height, bpp);
++
++ crop = &crop_array[ISP_CROP].rect;
++ bpp = crop_array[ISP_CROP].bpp;
++ val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_128);
++ isp_format_reg_list[6].addr = ISP_REG_DUMP_CFG_1;
++ isp_format_reg_list[6].val = val | 3 << 16;
++ isp_format_reg_list[6].mask = 0x0003FFFF;
++
++ st_info(ST_ISP, "raw left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++ crop->left, crop->top, crop->width, crop->height, bpp);
++
++ crop = &crop_array[ISP_SCALE_SS0].rect;
++ bpp = crop_array[ISP_SCALE_SS0].bpp;
++ isp_format_reg_list[7].addr = ISP_REG_SS0IW;
++ isp_format_reg_list[7].val = (crop->width << 16) + crop->height;
++ isp_format_reg_list[8].addr = ISP_REG_SS0S;
++ isp_format_reg_list[8].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++ st_info(ST_ISP, "ss0 left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++ crop->left, crop->top, crop->width, crop->height, bpp);
++
++ crop = &crop_array[ISP_SCALE_SS1].rect;
++ bpp = crop_array[ISP_SCALE_SS1].bpp;
++ isp_format_reg_list[9].addr = ISP_REG_SS1IW;
++ isp_format_reg_list[9].val = (crop->width << 16) + crop->height;
++ isp_format_reg_list[10].addr = ISP_REG_SS1S;
++ isp_format_reg_list[10].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++ crop = &crop_array[ISP_ITIWS].rect;
++ bpp = crop_array[ISP_ITIWS].bpp;
++ isp_format_reg_list[11].addr = ISP_REG_ITIIWSR;
++ isp_format_reg_list[11].val = (crop->height << 16) + crop->width;
++ isp_format_reg_list[12].addr = ISP_REG_ITIDWLSR;
++ isp_format_reg_list[12].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++ isp_format_reg_list[13].addr = ISP_REG_ITIDRLSR;
++ isp_format_reg_list[13].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++ st_info(ST_ISP, "iti left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++ crop->left, crop->top, crop->width, crop->height, bpp);
++
++ isp_format_reg_list[14].addr = ISP_REG_SENSOR;
++ isp_format_reg_list[14].val = 0x00000000;
++ if (type == DVP_SENSOR) {
++ unsigned int flags = dvp_dev->dvp->flags;
++
++ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++ isp_format_reg_list[14].val |= 0x08;
++ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
++ isp_format_reg_list[14].val |= 0x04;
++ } else {
++ isp_format_reg_list[14].val |= 0x01;
++ }
++
++ isp_load_regs(ispbase, isp_format_settings);
++ return 0;
++}
++
++static int stf_isp_stream_set(struct stf_isp_dev *isp_dev, int on)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++
++ void __iomem *ispbase;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ if (on) {
++#if defined(USE_NEW_CONFIG_SETTING)
++ isp_load_regs(ispbase, isp_reg_start_settings);
++#else
++ reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, 0x3FFFF, 0x3000a);
++ reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, BIT(1) | BIT(0), 0x3);
++ reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(0), 1);
++#endif //#if defined(USE_NEW_CONFIG_SETTING)
++ } else {
++ /* NOTE: Clear bit 0 of ISP_REG_ISP_CTRL_0 here will get crash. */
++ stf_isp_save_ctx_regs(isp_dev);
++ }
++
++ return 0;
++}
++
++static union reg_buf reg_buf;
++static int stf_isp_reg_read(struct stf_isp_dev *isp_dev, void *arg)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ struct isp_reg_param *reg_param = arg;
++ u32 size;
++ unsigned long r;
++
++ if (reg_param->reg_buf == NULL) {
++ st_err(ST_ISP, "Failed to access register. The pointer is NULL!!!\n");
++ return -EINVAL;
++ }
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ size = 0;
++ switch (reg_param->reg_info.method) {
++ case STF_ISP_REG_METHOD_ONE_REG:
++ break;
++
++ case STF_ISP_REG_METHOD_SERIES:
++ if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
++ return -EINVAL;
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_MODULE:
++ /* This mode is not supported in the V4L2 version. */
++ st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++ STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
++ return -ENOTTY;
++
++ case STF_ISP_REG_METHOD_TABLE:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 2;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_2:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 3;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_3:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 4;
++ break;
++
++ case STF_ISP_REG_METHOD_SMPL_PACK:
++ st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++ STF_ISP_REG_METHOD_SMPL_PACK is not supported!!!\n");
++ return -ENOTTY;
++
++ case STF_ISP_REG_METHOD_SOFT_RDMA:
++ // This mode is not supported in the V4L2 version.
++ st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++ STF_ISP_REG_METHOD_SOFT_RDMA is not supported!!!\n");
++ return -ENOTTY;
++
++ default:
++ st_err(ST_ISP, "Failed to access register. The method=%d \
++ is not supported!!!\n", reg_param->reg_info.method);
++ return -ENOTTY;
++ }
++
++ memset(®_buf, 0, sizeof(union reg_buf));
++ if (size) {
++ r = copy_from_user((u8 *)reg_buf.buffer,
++ (u8 *)reg_param->reg_buf->buffer, size);
++ if (r) {
++ st_err(ST_ISP, "Failed to call copy_from_user for the \
++ reg_param->reg_buf value\n");
++ return -EIO;
++ }
++ }
++
++ size = 0;
++ switch (reg_param->reg_info.method) {
++ case STF_ISP_REG_METHOD_ONE_REG:
++ reg_buf.buffer[0] = reg_read(ispbase, reg_param->reg_info.offset);
++ size = sizeof(u32);
++ break;
++
++ case STF_ISP_REG_METHOD_SERIES:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ reg_buf.buffer[r] = reg_read(ispbase,
++ reg_param->reg_info.offset + (r * 4));
++ }
++ size = sizeof(u32) * reg_param->reg_info.length;
++ break;
++
++ case STF_ISP_REG_METHOD_MODULE:
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ reg_buf.reg_tbl[r].value = reg_read(ispbase,
++ reg_buf.reg_tbl[r].offset);
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 2;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_2:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ if (reg_buf.reg_tbl2[r].mask) {
++ reg_buf.reg_tbl2[r].value = (reg_read(ispbase,
++ reg_buf.reg_tbl2[r].offset)
++ & reg_buf.reg_tbl2[r].mask);
++ } else {
++ reg_buf.reg_tbl2[r].value = reg_read(ispbase,
++ reg_buf.reg_tbl2[r].offset);
++ }
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 3;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_3:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ if (reg_buf.reg_tbl3[r].mask) {
++ reg_buf.reg_tbl3[r].value = (reg_read(ispbase,
++ reg_buf.reg_tbl3[r].offset)
++ & reg_buf.reg_tbl3[r].mask);
++ } else {
++ reg_buf.reg_tbl3[r].value = reg_read(ispbase,
++ reg_buf.reg_tbl3[r].offset);
++ }
++ if (reg_buf.reg_tbl3[r].delay_ms) {
++ usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
++ 1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
++ }
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 4;
++ break;
++
++ case STF_ISP_REG_METHOD_SMPL_PACK:
++ break;
++
++ case STF_ISP_REG_METHOD_SOFT_RDMA:
++ break;
++
++ default:
++ break;
++ }
++
++ r = copy_to_user((u8 *)reg_param->reg_buf->buffer, (u8 *)reg_buf.buffer,
++ size);
++ if (r) {
++ st_err(ST_ISP, "Failed to call copy_to_user for the \
++ reg_param->buffer value\n");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int stf_isp_soft_rdma(struct stf_isp_dev *isp_dev, u32 rdma_addr)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ struct isp_rdma_info *rdma_info = NULL;
++ s32 len;
++ u32 offset;
++ int ret = 0;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ rdma_info = phys_to_virt(rdma_addr);
++ while (1) {
++ if (rdma_info->tag == RDMA_WR_ONE) {
++ reg_write(ispbase, rdma_info->offset, rdma_info->param);
++ rdma_info++;
++ } else if (rdma_info->tag == RDMA_WR_SRL) {
++ offset = rdma_info->offset;
++ len = rdma_info->param;
++ rdma_info++;
++ while (len > 0) {
++ reg_write(ispbase, offset, rdma_info->param);
++ offset += 4;
++ len--;
++ if (len > 0) {
++ reg_write(ispbase, offset, rdma_info->value);
++ len--;
++ }
++ offset += 4;
++ rdma_info++;
++ }
++ } else if (rdma_info->tag == RDMA_LINK) {
++ rdma_info = phys_to_virt(rdma_info->param);
++ } else if (rdma_info->tag == RDMA_SINT) {
++ /* Software not support this command. */
++ rdma_info++;
++ } else if (rdma_info->tag == RDMA_END) {
++ break;
++ } else
++ rdma_info++;
++ }
++
++ return ret;
++}
++
++static int stf_isp_reg_write(struct stf_isp_dev *isp_dev, void *arg)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ struct isp_reg_param *reg_param = arg;
++ struct isp_rdma_info *rdma_info = NULL;
++ s32 len;
++ u32 offset;
++ u32 size;
++ unsigned long r;
++ int ret = 0;
++
++ if ((reg_param->reg_buf == NULL)
++ && (reg_param->reg_info.method != STF_ISP_REG_METHOD_SOFT_RDMA)) {
++ st_err(ST_ISP, "Failed to access register. \
++ The register buffer pointer is NULL!!!\n");
++ return -EINVAL;
++ }
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ size = 0;
++ switch (reg_param->reg_info.method) {
++ case STF_ISP_REG_METHOD_ONE_REG:
++ size = sizeof(u32);
++ break;
++
++ case STF_ISP_REG_METHOD_SERIES:
++ if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length;
++ break;
++
++ case STF_ISP_REG_METHOD_MODULE:
++ // This mode is not supported in the V4L2 version.
++ st_err(ST_ISP, "Reg Write - Failed to access register. \
++ The method = STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
++ return -ENOTTY;
++
++ case STF_ISP_REG_METHOD_TABLE:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 2;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_2:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 3;
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_3:
++ if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 4;
++ break;
++
++ case STF_ISP_REG_METHOD_SMPL_PACK:
++ if (reg_param->reg_info.length > STF_ISP_REG_SMPL_PACK_BUF_SIZE) {
++ st_err(ST_ISP, "Failed to access register. \
++ The (length=0x%08X > 0x%08X) is out of size!!!\n",
++ reg_param->reg_info.length, STF_ISP_REG_SMPL_PACK_BUF_SIZE);
++ return -EINVAL;
++ }
++ size = sizeof(u32) * reg_param->reg_info.length * 2;
++ break;
++
++ case STF_ISP_REG_METHOD_SOFT_RDMA:
++ break;
++
++ default:
++ st_err(ST_ISP, "Failed to access register. The method=%d \
++ is not supported!!!\n", reg_param->reg_info.method);
++ return -ENOTTY;
++ }
++
++ memset(®_buf, 0, sizeof(union reg_buf));
++ if (size) {
++ r = copy_from_user((u8 *)reg_buf.buffer,
++ (u8 *)reg_param->reg_buf->buffer, size);
++ if (r) {
++ st_err(ST_ISP, "Failed to call copy_from_user for the \
++ reg_param->reg_buf value\n");
++ return -EIO;
++ }
++ }
++
++ switch (reg_param->reg_info.method) {
++ case STF_ISP_REG_METHOD_ONE_REG:
++ reg_write(ispbase, reg_param->reg_info.offset, reg_buf.buffer[0]);
++ break;
++
++ case STF_ISP_REG_METHOD_SERIES:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ reg_write(ispbase, reg_param->reg_info.offset + (r * 4),
++ reg_buf.buffer[r]);
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_MODULE:
++ /* This mode is not supported in the V4L2 version. */
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ reg_write(ispbase, reg_buf.reg_tbl[r].offset,
++ reg_buf.reg_tbl[r].value);
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_2:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ if (reg_buf.reg_tbl2[r].mask) {
++ reg_set_bit(ispbase, reg_buf.reg_tbl2[r].offset,
++ reg_buf.reg_tbl2[r].mask, reg_buf.reg_tbl2[r].value);
++ } else {
++ reg_write(ispbase, reg_buf.reg_tbl2[r].offset,
++ reg_buf.reg_tbl2[r].value);
++ }
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_TABLE_3:
++ for (r = 0; r < reg_param->reg_info.length; r++) {
++ if (reg_buf.reg_tbl3[r].mask) {
++ reg_set_bit(ispbase, reg_buf.reg_tbl3[r].offset,
++ reg_buf.reg_tbl3[r].mask, reg_buf.reg_tbl3[r].value);
++ } else {
++ reg_write(ispbase, reg_buf.reg_tbl3[r].offset,
++ reg_buf.reg_tbl3[r].value);
++ }
++ if (reg_buf.reg_tbl3[r].delay_ms) {
++ usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
++ 1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
++ }
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_SMPL_PACK:
++ size = reg_param->reg_info.length;
++ rdma_info = ®_buf.rdma_cmd[0];
++ while (size) {
++ if (rdma_info->tag == RDMA_WR_ONE) {
++ reg_write(ispbase, rdma_info->offset, rdma_info->param);
++ rdma_info++;
++ size--;
++ } else if (rdma_info->tag == RDMA_WR_SRL) {
++ offset = rdma_info->offset;
++ len = rdma_info->param;
++ rdma_info++;
++ size--;
++ while (size && (len > 0)) {
++ reg_write(ispbase, offset, rdma_info->param);
++ offset += 4;
++ len--;
++ if (len > 0) {
++ reg_write(ispbase, offset, rdma_info->value);
++ len--;
++ }
++ offset += 4;
++ rdma_info++;
++ size--;
++ }
++ } else if (rdma_info->tag == RDMA_END) {
++ break;
++ } else {
++ rdma_info++;
++ size--;
++ }
++ }
++ break;
++
++ case STF_ISP_REG_METHOD_SOFT_RDMA:
++ /*
++ * Simulation the hardware RDMA behavior to debug and verify
++ * the RDMA chain.
++ */
++ ret = stf_isp_soft_rdma(isp_dev, reg_param->reg_info.offset);
++ break;
++
++ default:
++ break;
++ }
++
++ return ret;
++}
++
++static int stf_isp_shadow_trigger(struct stf_isp_dev *isp_dev)
++{
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase;
++
++ ispbase = stf_isp_get_ispbase(vin);
++
++ // shadow update
++ reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, (BIT(17) | BIT(16)), 0x30000);
++ reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, (BIT(1) | BIT(0)), 0x3);
++ return 0;
++}
++
++void dump_isp_reg(void *__iomem ispbase)
++{
++ int j;
++ u32 addr, val;
++
++ st_debug(ST_ISP, "DUMP ISP register:\n -- isp_reg_init_settings --\n");
++ for (j = 0; j < isp_reg_init_settings->regval_num; j++) {
++ addr = isp_reg_init_settings->regval[j].addr;
++ val = ioread32(ispbase + addr);
++ st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++ }
++
++ st_debug(ST_ISP, " --- isp_format_settings ---\n");
++ for (j = 0; j < isp_format_settings->regval_num; j++) {
++ addr = isp_format_settings->regval[j].addr;
++ val = ioread32(ispbase + addr);
++ st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++ }
++
++ val = ioread32(ispbase + ISP_REG_Y_PLANE_START_ADDR);
++ st_debug(ST_ISP, "-- ISP_REG_Y_PLANE_START_ADDR --\n {0x%08x, 0x%08x}\n",
++ ISP_REG_Y_PLANE_START_ADDR, val);
++ val = ioread32(ispbase + ISP_REG_UV_PLANE_START_ADDR);
++ st_debug(ST_ISP, "-- ISP_REG_UV_PLANE_START_ADDR --\n {0x%08x, 0x%08x}\n",
++ ISP_REG_UV_PLANE_START_ADDR, val);
++ val = ioread32(ispbase + ISP_REG_DUMP_CFG_0);
++ st_debug(ST_ISP, "-- ISP_REG_DUMP_CFG_0 --\n {0x%08x, 0x%08x}\n",
++ ISP_REG_DUMP_CFG_0, val);
++ val = ioread32(ispbase + ISP_REG_DUMP_CFG_1);
++ st_debug(ST_ISP, " --- ISP_REG_DUMP_CFG_1 ---\n {0x%08x, 0x%08x}\n",
++ ISP_REG_DUMP_CFG_1, val);
++
++ st_debug(ST_ISP, " --- isp_reg_start_settings ---\n");
++ for (j = 0; j < isp_reg_start_settings->regval_num; j++) {
++ addr = isp_reg_start_settings->regval[j].addr;
++ val = ioread32(ispbase + addr);
++ st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++ }
++}
++
++struct isp_hw_ops isp_ops = {
++ .isp_clk_enable = stf_isp_clk_enable,
++ .isp_clk_disable = stf_isp_clk_disable,
++ .isp_reset = stf_isp_reset,
++ .isp_config_set = stf_isp_config_set,
++ .isp_set_format = stf_isp_set_format,
++ .isp_stream_set = stf_isp_stream_set,
++ .isp_reg_read = stf_isp_reg_read,
++ .isp_reg_write = stf_isp_reg_write,
++ .isp_shadow_trigger = stf_isp_shadow_trigger,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
+@@ -0,0 +1,133 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_ISP_IOCTL_H
++#define STF_ISP_IOCTL_H
++
++
++#include <media/v4l2-ctrls.h>
++
++
++#define FILENAME_MAX_LEN 30
++
++#define ISP_IOC ('V')
++#define STF_ISP_REG_BUF_SIZE (768)
++#define STF_ISP_REG_TBL_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2)
++#define STF_ISP_REG_TBL_2_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 3)
++#define STF_ISP_REG_TBL_3_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 4)
++#define STF_ISP_REG_SMPL_PACK_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2)
++#define RDMA_WR_ONE (0xA0)
++#define RDMA_WR_SRL (0xA1)
++#define RDMA_LINK (0xA2)
++#define RDMA_SINT (0xA3)
++#define RDMA_END (0xAF)
++#define ENABLE_SS0_SS1
++
++enum _STF_ISP_IOCTL {
++ STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1,
++ STF_ISP_IOCTL_DMABUF_ALLOC,
++ STF_ISP_IOCTL_DMABUF_FREE,
++ STF_ISP_IOCTL_GET_HW_VER,
++ STF_ISP_IOCTL_REG,
++ STF_ISP_IOCTL_SHADOW_LOCK,
++ STF_ISP_IOCTL_SHADOW_UNLOCK,
++ STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER,
++ STF_ISP_IOCTL_SET_USER_CONFIG_ISP,
++ STF_ISP_IOCTL_MAX
++};
++
++enum _STF_ISP_REG_METHOD {
++ STF_ISP_REG_METHOD_ONE_REG = 0,
++ STF_ISP_REG_METHOD_SERIES,
++ STF_ISP_REG_METHOD_MODULE,
++ STF_ISP_REG_METHOD_TABLE,
++ STF_ISP_REG_METHOD_TABLE_2,
++ STF_ISP_REG_METHOD_TABLE_3,
++ STF_ISP_REG_METHOD_SMPL_PACK,
++ STF_ISP_REG_METHOD_SOFT_RDMA,
++ STF_ISP_REG_METHOD_MAX
++};
++
++
++struct stfisp_fw_info {
++ char __user filename[FILENAME_MAX_LEN];
++};
++
++struct dmabuf_create {
++ __u32 fd;
++ __u32 size;
++ __u32 paddr;
++};
++
++struct isp_rdma_info {
++ u32 param;
++ union {
++ u32 value;
++ struct {
++ u32 offset : 24;
++ u32 tag : 8;
++ };
++ };
++};
++
++struct isp_reg_info {
++ /** @brief [in] access method of register */
++ u8 method;
++ /** @brief [in] offset indicated which register will be read/write */
++ u32 offset;
++ /** @brief [in] length for indicated how much register will be read/write */
++ u32 length;
++};
++
++union reg_buf {
++ u32 buffer[STF_ISP_REG_BUF_SIZE];
++ struct {
++ u32 offset;
++ u32 value;
++ } reg_tbl[STF_ISP_REG_TBL_BUF_SIZE];
++ struct {
++ u32 offset;
++ u32 value;
++ u32 mask;
++ } reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE];
++ struct {
++ u32 offset;
++ u32 value;
++ u32 mask;
++ u32 delay_ms;
++ } reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE];
++ struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE];
++};
++
++struct isp_reg_param {
++ /** @brief [in, out] register read/write information */
++ struct isp_reg_info reg_info;
++ /** @brief [in, out] buffer */
++ union reg_buf *reg_buf;
++};
++
++
++#define VIDIOC_STFISP_LOAD_FW \
++ _IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info)
++#define VIDIOC_STF_DMABUF_ALLOC \
++ _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_ALLOC, struct dmabuf_create)
++#define VIDIOC_STF_DMABUF_FREE \
++ _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_FREE, struct dmabuf_create)
++#define VIDIOC_STFISP_GET_REG \
++ _IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
++#define VIDIOC_STFISP_SET_REG \
++ _IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
++#define VIDIOC_STFISP_SHADOW_LOCK \
++ _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK)
++#define VIDIOC_STFISP_SHADOW_UNLOCK \
++ _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK)
++#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \
++ _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER)
++#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \
++ _IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP)
++
++
++#endif /* STF_ISP_IOCTL_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -0,0 +1,1552 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include "stf_video.h"
++#include <media/media-entity.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mc.h>
++#include <media/videobuf2-dma-sg.h>
++#include <media/videobuf2-vmalloc.h>
++#include <media/videobuf2-dma-contig.h>
++
++static const struct stfcamss_format_info formats_pix_st7110_wr[] = {
++ { MEDIA_BUS_FMT_AYUV8_1X32, V4L2_PIX_FMT_AYUV32, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 32 } },
++ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
++ { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_PIX_FMT_RGB565, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
++ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++ { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++};
++
++static const struct stfcamss_format_info formats_raw_st7110_isp[] = {
++ { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++};
++
++static const struct stfcamss_format_info formats_pix_st7110_isp[] = {
++ // { MEDIA_BUS_FMT_YUYV12_2X12, V4L2_PIX_FMT_NV12M, 2,
++ // { { 1, 1 }, { 1, 1 } }, { { 1, 1 }, { 1, 1 } }, { 8 , 4 } },
++ { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1,
++ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++ { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV21, 1,
++ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++};
++
++static const struct stfcamss_format_info formats_st7110_isp_iti[] = {
++ // raw format
++ { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++ { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++ { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1,
++ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++
++ // YUV420
++ { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1,
++ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++ { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV21, 1,
++ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++
++ // YUV444
++ { MEDIA_BUS_FMT_YUV8_1X24, V4L2_PIX_FMT_NV24, 1,
++ { { 1, 1 } }, { { 1, 3 } }, { 8 } },
++ { MEDIA_BUS_FMT_VUY8_1X24, V4L2_PIX_FMT_NV42, 1,
++ { { 1, 1 } }, { { 1, 3 } }, { 8 } },
++};
++
++static int video_find_format(u32 code, u32 pixelformat,
++ const struct stfcamss_format_info *formats,
++ unsigned int nformats)
++{
++ int i;
++
++ for (i = 0; i < nformats; i++) {
++ if (formats[i].code == code &&
++ formats[i].pixelformat == pixelformat)
++ return i;
++ }
++
++ for (i = 0; i < nformats; i++)
++ if (formats[i].code == code)
++ return i;
++
++ for (i = 0; i < nformats; i++)
++ if (formats[i].pixelformat == pixelformat)
++ return i;
++
++ return -EINVAL;
++}
++
++static int __video_try_fmt(struct stfcamss_video *video,
++ struct v4l2_format *f, int is_mp)
++{
++ struct v4l2_pix_format *pix;
++ struct v4l2_pix_format_mplane *pix_mp;
++ const struct stfcamss_format_info *fi;
++ u32 width, height;
++ u32 bpl;
++ int i, j;
++
++ st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++ pix = &f->fmt.pix;
++ pix_mp = &f->fmt.pix_mp;
++
++ if (is_mp) {
++ for (i = 0; i < video->nformats; i++)
++ if (pix_mp->pixelformat
++ == video->formats[i].pixelformat)
++ break;
++
++ if (i == video->nformats)
++ i = 0; /* default format */
++
++ fi = &video->formats[i];
++ width = pix_mp->width;
++ height = pix_mp->height;
++
++ memset(pix_mp, 0, sizeof(*pix_mp));
++
++ pix_mp->pixelformat = fi->pixelformat;
++ pix_mp->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ pix_mp->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++ pix_mp->num_planes = fi->planes;
++ for (j = 0; j < pix_mp->num_planes; j++) {
++ bpl = pix_mp->width / fi->hsub[j].numerator *
++ fi->hsub[j].denominator * fi->bpp[j] / 8;
++ bpl = ALIGN(bpl, video->bpl_alignment);
++ pix_mp->plane_fmt[j].bytesperline = bpl;
++ pix_mp->plane_fmt[j].sizeimage = pix_mp->height /
++ fi->vsub[j].numerator
++ * fi->vsub[j].denominator * bpl;
++ }
++
++ pix_mp->field = V4L2_FIELD_NONE;
++ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
++ pix_mp->flags = 0;
++ pix_mp->ycbcr_enc =
++ V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
++ pix_mp->quantization =
++ V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ pix_mp->colorspace, pix_mp->ycbcr_enc);
++ pix_mp->xfer_func =
++ V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
++
++ st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", pix_mp->width,
++ pix_mp->height, fi->bpp[0]);
++ st_info(ST_VIDEO, "i = %d, p = %d, s = 0x%x\n", i,
++ pix_mp->num_planes, pix_mp->plane_fmt[0].sizeimage);
++
++ } else {
++ for (i = 0; i < video->nformats; i++)
++ if (pix->pixelformat == video->formats[i].pixelformat)
++ break;
++
++ if (i == video->nformats)
++ i = 0; /* default format */
++
++ fi = &video->formats[i];
++ width = pix->width;
++ height = pix->height;
++
++ memset(pix, 0, sizeof(*pix));
++
++ pix->pixelformat = fi->pixelformat;
++ pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
++ STFCAMSS_FRAME_MAX_WIDTH);
++ pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
++ STFCAMSS_FRAME_MAX_HEIGHT);
++ bpl = pix->width / fi->hsub[0].numerator *
++ fi->hsub[0].denominator * fi->bpp[0] / 8;
++ bpl = ALIGN(bpl, video->bpl_alignment);
++ pix->bytesperline = bpl;
++ pix->sizeimage = pix->height /
++ fi->vsub[0].numerator
++ * fi->vsub[0].denominator * bpl;
++
++ pix->field = V4L2_FIELD_NONE;
++ pix->colorspace = V4L2_COLORSPACE_SRGB;
++ pix->flags = 0;
++ pix->ycbcr_enc =
++ V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
++ pix->quantization =
++ V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ pix->colorspace, pix->ycbcr_enc);
++ pix->xfer_func =
++ V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
++
++ st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", pix->width,
++ pix->height, fi->bpp[0]);
++ st_info(ST_VIDEO, "i = %d, s = 0x%x\n", i, pix->sizeimage);
++ }
++ return 0;
++}
++
++static int stf_video_init_format(struct stfcamss_video *video, int is_mp)
++{
++ int ret;
++ struct v4l2_format format = {
++ .type = video->type,
++ .fmt.pix = {
++ .width = 1920,
++ .height = 1080,
++ .pixelformat = V4L2_PIX_FMT_RGB565,
++ },
++ };
++
++ ret = __video_try_fmt(video, &format, is_mp);
++
++ if (ret < 0)
++ return ret;
++
++ video->active_fmt = format;
++
++ return 0;
++}
++
++static int video_queue_setup(struct vb2_queue *q,
++ unsigned int *num_buffers, unsigned int *num_planes,
++ unsigned int sizes[], struct device *alloc_devs[])
++{
++ struct stfcamss_video *video = vb2_get_drv_priv(q);
++ const struct v4l2_pix_format *format =
++ &video->active_fmt.fmt.pix;
++ const struct v4l2_pix_format_mplane *format_mp =
++ &video->active_fmt.fmt.pix_mp;
++ unsigned int i;
++
++ st_debug(ST_VIDEO, "%s, planes = %d\n", __func__, *num_planes);
++
++ if (video->is_mp) {
++ if (*num_planes) {
++ if (*num_planes != format_mp->num_planes)
++ return -EINVAL;
++
++ for (i = 0; i < *num_planes; i++)
++ if (sizes[i] <
++ format_mp->plane_fmt[i].sizeimage)
++ return -EINVAL;
++
++ return 0;
++ }
++
++ *num_planes = format_mp->num_planes;
++
++ for (i = 0; i < *num_planes; i++)
++ sizes[i] = format_mp->plane_fmt[i].sizeimage;
++ } else {
++ if (*num_planes) {
++ if (*num_planes != 1)
++ return -EINVAL;
++
++ if (sizes[0] < format->sizeimage)
++ return -EINVAL;
++ }
++
++ *num_planes = 1;
++ sizes[0] = format->sizeimage;
++ if (!sizes[0])
++ st_err(ST_VIDEO, "%s: error size is zero!!!\n", __func__);
++ }
++ if ((stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC)
++ == STF_ISP_PAD_SRC_SCD_Y) &&
++ sizes[0] < ISP_SCD_Y_BUFFER_SIZE) {
++ sizes[0] = ISP_SCD_Y_BUFFER_SIZE;
++ }
++
++ st_info(ST_VIDEO, "%s, planes = %d, size = %d\n",
++ __func__, *num_planes, sizes[0]);
++ return 0;
++}
++
++static int video_buf_init(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++ struct stfcamss_buffer *buffer =
++ container_of(vbuf, struct stfcamss_buffer, vb);
++ const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
++ const struct v4l2_pix_format_mplane *fmt_mp =
++ &video->active_fmt.fmt.pix_mp;
++ //struct sg_table *sgt;
++ dma_addr_t *paddr;
++ unsigned int i;
++
++ buffer->sizeimage = 0;
++
++ if (video->is_mp) {
++ for (i = 0; i < fmt_mp->num_planes; i++) {
++ paddr = vb2_plane_cookie(vb, i);
++ buffer->addr[i] = *paddr;
++ buffer->sizeimage += vb2_plane_size(vb, i);
++ }
++
++ if (fmt_mp->num_planes == 1
++ && (fmt_mp->pixelformat == V4L2_PIX_FMT_NV12
++ || fmt_mp->pixelformat == V4L2_PIX_FMT_NV21
++ || fmt_mp->pixelformat == V4L2_PIX_FMT_NV16
++ || fmt_mp->pixelformat == V4L2_PIX_FMT_NV61))
++ buffer->addr[1] = buffer->addr[0] +
++ fmt_mp->plane_fmt[0].bytesperline *
++ fmt_mp->height;
++ } else {
++ paddr = vb2_plane_cookie(vb, 0);
++ buffer->sizeimage = vb2_plane_size(vb, 0);
++ buffer->addr[0] = *paddr;
++ if (fmt->pixelformat == V4L2_PIX_FMT_NV12
++ || fmt->pixelformat == V4L2_PIX_FMT_NV21
++ || fmt->pixelformat == V4L2_PIX_FMT_NV16
++ || fmt->pixelformat == V4L2_PIX_FMT_NV61)
++ buffer->addr[1] = buffer->addr[0] +
++ fmt->bytesperline *
++ fmt->height;
++ }
++
++ if (stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC)
++ == STF_ISP_PAD_SRC_SCD_Y) {
++ buffer->addr[1] = buffer->addr[0] + ISP_YHIST_BUFFER_SIZE;
++ buffer->vaddr_sc = vb2_plane_vaddr(vb, 0);
++ }
++
++ return 0;
++}
++
++static int video_buf_prepare(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++ const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
++ const struct v4l2_pix_format_mplane *fmt_mp =
++ &video->active_fmt.fmt.pix_mp;
++ unsigned int i;
++
++ if (video->is_mp) {
++ for (i = 0; i < fmt_mp->num_planes; i++) {
++ if (fmt_mp->plane_fmt[i].sizeimage
++ > vb2_plane_size(vb, i))
++ return -EINVAL;
++
++ vb2_set_plane_payload(vb, i,
++ fmt_mp->plane_fmt[i].sizeimage);
++ }
++ } else {
++ if (fmt->sizeimage > vb2_plane_size(vb, 0)) {
++ st_err(ST_VIDEO, "sizeimage = %d, plane size = %d\n",
++ fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0));
++ return -EINVAL;
++ }
++ vb2_set_plane_payload(vb, 0, fmt->sizeimage);
++ }
++
++ vbuf->field = V4L2_FIELD_NONE;
++
++ return 0;
++}
++
++static void video_buf_queue(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++ struct stfcamss_buffer *buffer =
++ container_of(vbuf, struct stfcamss_buffer, vb);
++
++ video->ops->queue_buffer(video, buffer);
++}
++
++static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
++ struct v4l2_pix_format_mplane *pix,
++ const struct stfcamss_format_info *f,
++ unsigned int alignment)
++{
++ unsigned int i;
++ u32 bytesperline;
++
++ memset(pix, 0, sizeof(*pix));
++ v4l2_fill_pix_format_mplane(pix, mbus);
++ pix->pixelformat = f->pixelformat;
++ pix->num_planes = f->planes;
++ for (i = 0; i < pix->num_planes; i++) {
++ bytesperline = pix->width / f->hsub[i].numerator *
++ f->hsub[i].denominator * f->bpp[i] / 8;
++ bytesperline = ALIGN(bytesperline, alignment);
++ pix->plane_fmt[i].bytesperline = bytesperline;
++ pix->plane_fmt[i].sizeimage = pix->height /
++ f->vsub[i].numerator * f->vsub[i].denominator *
++ bytesperline;
++ }
++
++ return 0;
++}
++
++static int video_mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
++ struct v4l2_pix_format *pix,
++ const struct stfcamss_format_info *f,
++ unsigned int alignment)
++{
++ u32 bytesperline;
++
++ memset(pix, 0, sizeof(*pix));
++ v4l2_fill_pix_format(pix, mbus);
++ pix->pixelformat = f->pixelformat;
++ bytesperline = pix->width / f->hsub[0].numerator *
++ f->hsub[0].denominator * f->bpp[0] / 8;
++ bytesperline = ALIGN(bytesperline, alignment);
++ pix->bytesperline = bytesperline;
++ pix->sizeimage = pix->height /
++ f->vsub[0].numerator * f->vsub[0].denominator *
++ bytesperline;
++ return 0;
++}
++
++static struct v4l2_subdev *video_remote_subdev(
++ struct stfcamss_video *video, u32 *pad)
++{
++ struct media_pad *remote;
++
++ remote = media_pad_remote_pad_first(&video->pad);
++
++ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
++ return NULL;
++
++ if (pad)
++ *pad = remote->index;
++
++ return media_entity_to_v4l2_subdev(remote->entity);
++}
++
++static int video_get_subdev_format(struct stfcamss_video *video,
++ struct v4l2_format *format)
++{
++ struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
++ struct v4l2_pix_format_mplane *pix_mp =
++ &video->active_fmt.fmt.pix_mp;
++ struct v4l2_subdev_format fmt;
++ struct v4l2_subdev *subdev;
++ u32 pixelformat;
++ u32 pad;
++ int ret;
++
++ subdev = video_remote_subdev(video, &pad);
++ if (subdev == NULL)
++ return -EPIPE;
++
++ fmt.pad = pad;
++ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
++ if (ret)
++ return ret;
++
++ if (video->is_mp)
++ pixelformat = pix_mp->pixelformat;
++ else
++ pixelformat = pix->pixelformat;
++ ret = video_find_format(fmt.format.code, pixelformat,
++ video->formats, video->nformats);
++ if (ret < 0)
++ return ret;
++
++ format->type = video->type;
++
++ if (video->is_mp)
++ return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
++ &video->formats[ret], video->bpl_alignment);
++ else
++ return video_mbus_to_pix(&fmt.format, &format->fmt.pix,
++ &video->formats[ret], video->bpl_alignment);
++}
++
++static int video_check_format(struct stfcamss_video *video)
++{
++ struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
++ struct v4l2_pix_format_mplane *pix_mp =
++ &video->active_fmt.fmt.pix_mp;
++ struct v4l2_format format;
++ struct v4l2_pix_format *sd_pix = &format.fmt.pix;
++ struct v4l2_pix_format_mplane *sd_pix_mp = &format.fmt.pix_mp;
++ int ret;
++
++ if (video->is_mp) {
++ sd_pix_mp->pixelformat = pix_mp->pixelformat;
++ ret = video_get_subdev_format(video, &format);
++ if (ret < 0)
++ return ret;
++
++ if (pix_mp->pixelformat != sd_pix_mp->pixelformat ||
++ pix_mp->height > sd_pix_mp->height ||
++ pix_mp->width > sd_pix_mp->width ||
++ pix_mp->num_planes != sd_pix_mp->num_planes ||
++ pix_mp->field != format.fmt.pix_mp.field) {
++ st_err(ST_VIDEO,
++ "%s, not match:\n"
++ "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n",
++ __func__,
++ pix_mp->pixelformat, sd_pix_mp->pixelformat,
++ pix_mp->height, sd_pix_mp->height,
++ pix_mp->field, format.fmt.pix_mp.field);
++ return -EPIPE;
++ }
++
++ } else {
++ sd_pix->pixelformat = pix->pixelformat;
++ ret = video_get_subdev_format(video, &format);
++ if (ret < 0)
++ return ret;
++
++ if (pix->pixelformat != sd_pix->pixelformat ||
++ pix->height > sd_pix->height ||
++ pix->width > sd_pix->width ||
++ pix->field != format.fmt.pix.field) {
++ st_err(ST_VIDEO,
++ "%s, not match:\n"
++ "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n",
++ __func__,
++ pix->pixelformat, sd_pix->pixelformat,
++ pix->height, sd_pix->height,
++ pix->field, format.fmt.pix.field);
++ return -EPIPE;
++ }
++ }
++ return 0;
++}
++
++static int video_start_streaming(struct vb2_queue *q, unsigned int count)
++{
++ struct stfcamss_video *video = vb2_get_drv_priv(q);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct media_pad *pad;
++ struct v4l2_subdev *subdev;
++ int ret;
++
++ ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
++ if (ret < 0) {
++ st_err(ST_VIDEO,
++ "Failed to video_device_pipeline_start: %d\n", ret);
++ return ret;
++ }
++
++ ret = video_check_format(video);
++ if (ret < 0)
++ goto error;
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ goto error;
++ }
++ return 0;
++
++error:
++ video_device_pipeline_stop(vdev);
++ video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
++ return ret;
++}
++
++static void video_stop_streaming(struct vb2_queue *q)
++{
++ struct stfcamss_video *video = vb2_get_drv_priv(q);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct media_pad *pad;
++ struct v4l2_subdev *subdev;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ v4l2_subdev_call(subdev, video, s_stream, 0);
++ }
++
++ video_device_pipeline_stop(vdev);
++ video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
++}
++
++static const struct vb2_ops stf_video_vb2_q_ops = {
++ .queue_setup = video_queue_setup,
++ .wait_prepare = vb2_ops_wait_prepare,
++ .wait_finish = vb2_ops_wait_finish,
++ .buf_init = video_buf_init,
++ .buf_prepare = video_buf_prepare,
++ .buf_queue = video_buf_queue,
++ .start_streaming = video_start_streaming,
++ .stop_streaming = video_stop_streaming,
++};
++
++/* -----------------------------------------------------
++ * V4L2 ioctls
++ */
++
++static int getcrop_pad_id(int video_id)
++{
++ return stf_vin_map_isp_pad(video_id, STF_ISP_PAD_SRC);
++}
++
++static int video_querycap(struct file *file, void *fh,
++ struct v4l2_capability *cap)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++
++ strscpy(cap->driver, "stf camss", sizeof(cap->driver));
++ strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card));
++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++ dev_name(video->stfcamss->dev));
++ return 0;
++}
++
++static int video_get_unique_pixelformat_by_index(struct stfcamss_video *video,
++ int ndx)
++{
++ int i, j, k;
++
++ /* find index "i" of "k"th unique pixelformat in formats array */
++ k = -1;
++ for (i = 0; i < video->nformats; i++) {
++ for (j = 0; j < i; j++) {
++ if (video->formats[i].pixelformat ==
++ video->formats[j].pixelformat)
++ break;
++ }
++
++ if (j == i)
++ k++;
++
++ if (k == ndx)
++ return i;
++ }
++
++ return -EINVAL;
++}
++
++static int video_get_pixelformat_by_mbus_code(struct stfcamss_video *video,
++ u32 mcode)
++{
++ int i;
++
++ for (i = 0; i < video->nformats; i++) {
++ if (video->formats[i].code == mcode)
++ return i;
++ }
++
++ return -EINVAL;
++}
++
++static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ int i;
++
++ st_debug(ST_VIDEO, "%s:\n0x%x 0x%x\n 0x%x, 0x%x\n0x%x\n",
++ __func__,
++ f->type, video->type,
++ f->index, video->nformats,
++ f->mbus_code);
++
++ if (f->type != video->type)
++ return -EINVAL;
++ if (f->index >= video->nformats)
++ return -EINVAL;
++
++ if (f->mbus_code) {
++ /* Each entry in formats[] table has unique mbus_code */
++ if (f->index > 0)
++ return -EINVAL;
++
++ i = video_get_pixelformat_by_mbus_code(video, f->mbus_code);
++ } else {
++ i = video_get_unique_pixelformat_by_index(video, f->index);
++ }
++
++ if (i < 0)
++ return -EINVAL;
++
++ f->pixelformat = video->formats[i].pixelformat;
++
++ return 0;
++}
++
++static int video_enum_framesizes(struct file *file, void *fh,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct v4l2_subdev_frame_size_enum fse = {0};
++ struct v4l2_subdev_mbus_code_enum code = {0};
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity = &vdev->entity;
++ struct media_entity *sensor;
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ bool support_selection = false;
++ int i;
++ int ret;
++
++ for (i = 0; i < video->nformats; i++) {
++ if (video->formats[i].pixelformat == fsize->pixel_format)
++ break;
++ }
++
++ if (i == video->nformats)
++ return -EINVAL;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ if (subdev->ops->pad->set_selection) {
++ support_selection = true;
++ break;
++ }
++ }
++
++ if (support_selection) {
++ if (fsize->index)
++ return -EINVAL;
++ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
++ fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
++ fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
++ fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
++ fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
++ fsize->stepwise.step_width = 1;
++ fsize->stepwise.step_height = 1;
++ } else {
++ entity = &vdev->entity;
++ sensor = stfcamss_find_sensor(entity);
++ if (!sensor)
++ return -ENOTTY;
++
++ subdev = media_entity_to_v4l2_subdev(sensor);
++ code.index = 0;
++ code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code);
++ if (ret < 0)
++ return -EINVAL;
++ fse.index = fsize->index;
++ fse.code = code.code;
++ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++ ret = v4l2_subdev_call(subdev, pad, enum_frame_size, NULL, &fse);
++ if (ret < 0)
++ return -EINVAL;
++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++ fsize->discrete.width = fse.min_width;
++ fsize->discrete.height = fse.min_height;
++ }
++
++ return 0;
++}
++
++static int video_enum_frameintervals(struct file *file, void *fh,
++ struct v4l2_frmivalenum *fival)
++{
++ int ret = 0;
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity = &vdev->entity;
++ struct media_entity *sensor;
++ struct v4l2_subdev *subdev;
++ struct v4l2_subdev_mbus_code_enum code = {0};
++ struct v4l2_subdev_frame_interval_enum fie = {0};
++
++ sensor = stfcamss_find_sensor(entity);
++ if (!sensor)
++ return -ENOTTY;
++ fie.index = fival->index;
++ fie.width = fival->width;
++ fie.height = fival->height;
++ fie.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++ subdev = media_entity_to_v4l2_subdev(sensor);
++
++ code.index = 0;
++ code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
++ /* Don't care about the code, just find by pixelformat */
++ ret = video_find_format(0, fival->pixel_format,
++ video->formats, video->nformats);
++ if (ret < 0)
++ return -EINVAL;
++
++ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code);
++ if (ret < 0)
++ return -EINVAL;
++
++ fie.code = code.code;
++ ret = v4l2_subdev_call(subdev, pad, enum_frame_interval, NULL, &fie);
++ if (ret < 0)
++ return ret;
++
++ fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++ fival->discrete = fie.interval;
++
++ return 0;
++}
++
++static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++
++ st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++ st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x,0x%x\n",
++ __func__, video->active_fmt.type,
++ video->active_fmt.fmt.pix.pixelformat);
++ *f = video->active_fmt;
++ return 0;
++}
++
++static int video_g_fmt_mp(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++
++ st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++ st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x\n",
++ __func__, video->active_fmt.type);
++ *f = video->active_fmt;
++ return 0;
++}
++
++static int video_entity_s_fmt(struct stfcamss_video *video,
++ struct media_entity *entity,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ struct v4l2_mbus_framefmt *mf = &fmt->format;
++ struct v4l2_subdev_format fmt_src = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ };
++ u32 width, height, code;
++ int ret, index = 0;
++
++ code = mf->code;
++ width = mf->width;
++ height = mf->height;
++ subdev = media_entity_to_v4l2_subdev(entity);
++ while (1) {
++ if (index >= entity->num_pads)
++ break;
++ pad = &entity->pads[index];
++ pad = media_pad_remote_pad_first(pad);
++ if (pad && is_media_entity_v4l2_subdev(pad->entity)) {
++ fmt->pad = index;
++ ret = v4l2_subdev_call(subdev, pad, set_fmt, state, fmt);
++ if (mf->code != code ||
++ mf->width != width || mf->height != height) {
++ st_warn(ST_VIDEO,
++ "\"%s\":%d pad fmt has been"
++ " changed to 0x%x %ux%u\n",
++ subdev->name, fmt->pad, mf->code,
++ mf->width, mf->height);
++ }
++ if (index) {
++ fmt_src.pad = index;
++ ret = v4l2_subdev_call(subdev, pad, get_fmt, state, &fmt_src);
++ if (ret)
++ return ret;
++
++ fmt->format.code = fmt_src.format.code;
++ ret = video_entity_s_fmt(video, pad->entity, state, fmt);
++ }
++ }
++
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ break;
++ index++;
++ }
++ return ret;
++}
++
++static int video_pipeline_s_fmt(struct stfcamss_video *video,
++ struct v4l2_subdev_state *state,
++ struct v4l2_format *f)
++{
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity = &vdev->entity;
++ struct v4l2_subdev *subdev;
++ int ret, index;
++ struct v4l2_subdev_format fmt = {
++ .pad = 0,
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .reserved = {getcrop_pad_id(video->id)}
++ };
++ struct v4l2_mbus_framefmt *mf = &fmt.format;
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
++ struct media_entity *sensor;
++ u32 width, height;
++ struct media_pad *pad;
++
++ /* pix to mbus format */
++ if (video->is_mp) {
++ index = video_find_format(mf->code,
++ pix_mp->pixelformat,
++ video->formats, video->nformats);
++ if (index < 0)
++ return index;
++ v4l2_fill_mbus_format_mplane(mf, pix_mp);
++ mf->code = video->formats[index].code;
++ } else {
++ index = video_find_format(mf->code,
++ pix->pixelformat,
++ video->formats, video->nformats);
++ if (index < 0)
++ return index;
++ v4l2_fill_mbus_format(mf, pix, video->formats[index].code);
++ }
++
++ width = mf->width;
++ height = mf->height;
++
++ sensor = stfcamss_find_sensor(entity);
++ if (!sensor) {
++ st_err(ST_VIDEO, "Can't find sensor\n");
++ return -ENOTTY;
++ }
++
++ subdev = media_entity_to_v4l2_subdev(sensor);
++ ret = v4l2_subdev_call(subdev, pad, get_fmt, state, &fmt);
++ if (ret)
++ return ret;
++
++ /*
++ * Starting from sensor subdevice, walk within
++ * pipeline and set format on each subdevice
++ */
++ pad = media_pad_remote_pad_first(&sensor->pads[0]);
++ ret = video_entity_s_fmt(video, pad->entity, state, &fmt);
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ return ret;
++
++ index = video_find_format(mf->code,
++ video->formats[index].pixelformat,
++ video->formats, video->nformats);
++ st_debug(ST_VIDEO, "%s, code=%x, index=%d\n",
++ __func__, mf->code, index);
++
++ if (index < 0)
++ return index;
++
++ if (video->is_mp)
++ video_mbus_to_pix_mp(mf, pix_mp,
++ &video->formats[index], video->bpl_alignment);
++ else
++ video_mbus_to_pix(mf, pix,
++ &video->formats[index], video->bpl_alignment);
++
++ ret = __video_try_fmt(video, f, video->is_mp);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ int ret;
++
++ st_debug(ST_VIDEO, "%s, fmt.type = 0x%x, v4l2fmt=%x\n",
++ __func__, f->type, f->fmt.pix.pixelformat);
++
++ if (vb2_is_busy(&video->vb2_q))
++ return -EBUSY;
++
++ ret = __video_try_fmt(video, f, false);
++ if (ret < 0)
++ return ret;
++
++ ret = video_pipeline_s_fmt(video, NULL, f);
++
++ st_debug(ST_VIDEO, "%s, pixelformat=0x%x, ret=%d\n",
++ __func__, f->fmt.pix.pixelformat, ret);
++ if (ret < 0)
++ return ret;
++
++ video->active_fmt = *f;
++
++ return 0;
++}
++
++static int video_s_fmt_mp(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ int ret;
++
++ st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++ if (vb2_is_busy(&video->vb2_q))
++ return -EBUSY;
++
++ ret = __video_try_fmt(video, f, true);
++ if (ret < 0)
++ return ret;
++
++ ret = video_pipeline_s_fmt(video, NULL, f);
++ if (ret < 0)
++ return ret;
++
++ video->active_fmt = *f;
++
++ return 0;
++}
++
++static int video_try_fmt(struct file *file,
++ void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++
++ return __video_try_fmt(video, f, false);
++}
++
++static int video_try_fmt_mp(struct file *file,
++ void *fh, struct v4l2_format *f)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++
++ return __video_try_fmt(video, f, true);
++}
++
++static int video_enum_input(struct file *file, void *fh,
++ struct v4l2_input *input)
++{
++ if (input->index > 0)
++ return -EINVAL;
++
++ strscpy(input->name, "camera", sizeof(input->name));
++ input->type = V4L2_INPUT_TYPE_CAMERA;
++
++ return 0;
++}
++
++static int video_g_input(struct file *file, void *fh, unsigned int *input)
++{
++ *input = 0;
++
++ return 0;
++}
++
++static int video_s_input(struct file *file, void *fh, unsigned int input)
++{
++ return input == 0 ? 0 : -EINVAL;
++}
++
++static int video_g_parm(struct file *file, void *priv,
++ struct v4l2_streamparm *p)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ int ret, is_support = 0;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ ret = v4l2_g_parm_cap(vdev, subdev, p);
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ break;
++ if (!ret)
++ is_support = 1;
++ }
++
++ return is_support ? 0 : ret;
++}
++
++static int video_s_parm(struct file *file, void *priv,
++ struct v4l2_streamparm *p)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ struct v4l2_streamparm tmp_p;
++ int ret, is_support = 0;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ tmp_p = *p;
++ ret = v4l2_s_parm_cap(vdev, subdev, &tmp_p);
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ break;
++ if (!ret) {
++ is_support = 1;
++ *p = tmp_p;
++ }
++ }
++
++ return is_support ? 0 : ret;
++}
++
++/* Crop ioctls */
++int video_g_pixelaspect(struct file *file, void *fh,
++ int buf_type, struct v4l2_fract *aspect)
++{
++ return 0;
++}
++
++int video_g_selection(struct file *file, void *fh,
++ struct v4l2_selection *s)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ struct v4l2_subdev_selection sel = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .pad = getcrop_pad_id(video->id),
++ .target = s->target,
++ .r = s->r,
++ .flags = s->flags,
++ };
++ int ret;
++
++ st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n",
++ __func__, sel.target, s->target);
++ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
++ && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++ return -EINVAL;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sel);
++ if (!ret) {
++ s->r = sel.r;
++ s->flags = sel.flags;
++ break;
++ }
++ if (ret != -ENOIOCTLCMD)
++ break;
++ }
++
++ return ret;
++}
++
++int video_s_selection(struct file *file, void *fh,
++ struct v4l2_selection *s)
++{
++ struct stfcamss_video *video = video_drvdata(file);
++ struct video_device *vdev = &video->vdev;
++ struct media_entity *entity;
++ struct v4l2_subdev *subdev;
++ struct media_pad *pad;
++ struct v4l2_subdev_selection sel = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .pad = getcrop_pad_id(video->id),
++ .target = s->target,
++ .r = s->r,
++ .flags = s->flags,
++ };
++ struct v4l2_pix_format *format = &video->active_fmt.fmt.pix;
++ struct v4l2_pix_format_mplane *format_mp =
++ &video->active_fmt.fmt.pix_mp;
++ int ret;
++
++ st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n",
++ __func__, sel.target, s->target);
++ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
++ && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++ return -EINVAL;
++
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sel);
++ if (!ret) {
++ s->r = sel.r;
++ s->flags = sel.flags;
++ format->width = s->r.width;
++ format->height = s->r.height;
++ format_mp->width = s->r.width;
++ format_mp->height = s->r.height;
++ ret = __video_try_fmt(video, &video->active_fmt,
++ video->is_mp);
++ if (ret < 0)
++ return ret;
++ break;
++ }
++ if (ret != -ENOIOCTLCMD)
++ break;
++ }
++
++ st_debug(ST_VIDEO, "ret = 0x%x, -EINVAL = 0x%x\n", ret, -EINVAL);
++
++ return ret;
++}
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
++ .vidioc_querycap = video_querycap,
++ .vidioc_enum_fmt_vid_cap = video_enum_fmt,
++ .vidioc_enum_framesizes = video_enum_framesizes,
++ .vidioc_enum_frameintervals = video_enum_frameintervals,
++ .vidioc_g_fmt_vid_cap = video_g_fmt,
++ .vidioc_s_fmt_vid_cap = video_s_fmt,
++ .vidioc_try_fmt_vid_cap = video_try_fmt,
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++ .vidioc_enum_input = video_enum_input,
++ .vidioc_g_input = video_g_input,
++ .vidioc_s_input = video_s_input,
++ .vidioc_g_parm = video_g_parm,
++ .vidioc_s_parm = video_s_parm,
++ .vidioc_s_selection = video_s_selection,
++ .vidioc_g_selection = video_g_selection,
++};
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
++ .vidioc_querycap = video_querycap,
++ .vidioc_enum_fmt_vid_cap = video_enum_fmt,
++ .vidioc_enum_framesizes = video_enum_framesizes,
++ .vidioc_enum_frameintervals = video_enum_frameintervals,
++ .vidioc_g_fmt_vid_cap_mplane = video_g_fmt_mp,
++ .vidioc_s_fmt_vid_cap_mplane = video_s_fmt_mp,
++ .vidioc_try_fmt_vid_cap_mplane = video_try_fmt_mp,
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++ .vidioc_enum_input = video_enum_input,
++ .vidioc_g_input = video_g_input,
++ .vidioc_s_input = video_s_input,
++ .vidioc_g_parm = video_g_parm,
++ .vidioc_s_parm = video_s_parm,
++ .vidioc_s_selection = video_s_selection,
++ .vidioc_g_selection = video_g_selection,
++};
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
++ .vidioc_querycap = video_querycap,
++ .vidioc_enum_fmt_vid_out = video_enum_fmt,
++ .vidioc_enum_framesizes = video_enum_framesizes,
++ .vidioc_enum_frameintervals = video_enum_frameintervals,
++ .vidioc_g_fmt_vid_out = video_g_fmt,
++ .vidioc_s_fmt_vid_out = video_s_fmt,
++ .vidioc_try_fmt_vid_out = video_try_fmt,
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++};
++
++static int video_open(struct file *file)
++{
++ struct video_device *vdev = video_devdata(file);
++ struct stfcamss_video *video = video_drvdata(file);
++ struct v4l2_fh *vfh;
++ int ret;
++
++ mutex_lock(&video->lock);
++
++ vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
++ if (vfh == NULL) {
++ ret = -ENOMEM;
++ goto error_alloc;
++ }
++
++ v4l2_fh_init(vfh, vdev);
++ v4l2_fh_add(vfh);
++
++ file->private_data = vfh;
++
++ if (!video->pm_count) {
++ ret = v4l2_pipeline_pm_get(&vdev->entity);
++ if (ret < 0) {
++ st_err(ST_VIDEO,
++ "Failed to power up pipeline: %d\n", ret);
++ goto error_pm_use;
++ }
++ }
++
++ video->pm_count++;
++
++ mutex_unlock(&video->lock);
++
++ return 0;
++
++error_pm_use:
++ v4l2_fh_release(file);
++error_alloc:
++ mutex_unlock(&video->lock);
++ return ret;
++}
++
++static int video_release(struct file *file)
++{
++ struct video_device *vdev = video_devdata(file);
++ struct stfcamss_video *video = video_drvdata(file);
++
++ vb2_fop_release(file);
++
++ video->pm_count--;
++
++ if (!video->pm_count)
++ v4l2_pipeline_pm_put(&vdev->entity);
++
++ file->private_data = NULL;
++
++ return 0;
++}
++
++static const struct v4l2_file_operations stf_vid_fops = {
++ .owner = THIS_MODULE,
++ .unlocked_ioctl = video_ioctl2,
++ .open = video_open,
++ .release = video_release,
++ .poll = vb2_fop_poll,
++ .mmap = vb2_fop_mmap,
++ .read = vb2_fop_read,
++};
++
++static void stf_video_release(struct video_device *vdev)
++{
++ struct stfcamss_video *video = video_get_drvdata(vdev);
++
++ media_entity_cleanup(&vdev->entity);
++
++ mutex_destroy(&video->q_lock);
++ mutex_destroy(&video->lock);
++}
++
++int stf_video_register(struct stfcamss_video *video,
++ struct v4l2_device *v4l2_dev,
++ const char *name, int is_mp)
++{
++ struct video_device *vdev;
++ struct vb2_queue *q;
++ struct media_pad *pad = &video->pad;
++ int ret;
++ enum isp_pad_id isp_pad;
++
++ vdev = &video->vdev;
++
++ mutex_init(&video->q_lock);
++
++ q = &video->vb2_q;
++ q->drv_priv = video;
++ q->mem_ops = &vb2_dma_contig_memops;
++ q->ops = &stf_video_vb2_q_ops;
++ //q->type = is_mp ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
++ // V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ q->type = video->type;
++ q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++ q->buf_struct_size = sizeof(struct stfcamss_buffer);
++ q->dev = video->stfcamss->dev;
++ q->lock = &video->q_lock;
++ q->min_buffers_needed = STFCAMSS_MIN_BUFFERS;
++ ret = vb2_queue_init(q);
++ if (ret < 0) {
++ st_err(ST_VIDEO,
++ "Failed to init vb2 queue: %d\n", ret);
++ goto err_vb2_init;
++ }
++
++ pad->flags = MEDIA_PAD_FL_SINK;
++ ret = media_entity_pads_init(&vdev->entity, 1, pad);
++ if (ret < 0) {
++ st_err(ST_VIDEO,
++ "Failed to init video entity: %d\n",
++ ret);
++ goto err_vb2_init;
++ }
++
++ mutex_init(&video->lock);
++
++ isp_pad = stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC);
++ if (video->id == VIN_LINE_WR) {
++ video->formats = formats_pix_st7110_wr;
++ video->nformats = ARRAY_SIZE(formats_pix_st7110_wr);
++ video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++ } else if (isp_pad == STF_ISP_PAD_SRC
++ || isp_pad == STF_ISP_PAD_SRC_SS0
++ || isp_pad == STF_ISP_PAD_SRC_SS1) {
++ video->formats = formats_pix_st7110_isp;
++ video->nformats = ARRAY_SIZE(formats_pix_st7110_isp);
++ video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++ } else if (isp_pad == STF_ISP_PAD_SRC_ITIW
++ || isp_pad == STF_ISP_PAD_SRC_ITIR) {
++ video->formats = formats_st7110_isp_iti;
++ video->nformats = ARRAY_SIZE(formats_st7110_isp_iti);
++ video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++ } else { // raw/scdump/yhist
++ video->formats = formats_raw_st7110_isp;
++ video->nformats = ARRAY_SIZE(formats_raw_st7110_isp);
++ video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_128;
++ }
++ video->is_mp = is_mp;
++
++ ret = stf_video_init_format(video, is_mp);
++ if (ret < 0) {
++ st_err(ST_VIDEO, "Failed to init format: %d\n", ret);
++ goto err_vid_init_format;
++ }
++
++ vdev->fops = &stf_vid_fops;
++ if (isp_pad == STF_ISP_PAD_SRC_ITIR) {
++ vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT;
++ vdev->vfl_dir = VFL_DIR_TX;
++ } else {
++ vdev->device_caps = is_mp ? V4L2_CAP_VIDEO_CAPTURE_MPLANE :
++ V4L2_CAP_VIDEO_CAPTURE;
++ vdev->vfl_dir = VFL_DIR_RX;
++ }
++ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
++ if (video->type == V4L2_CAP_VIDEO_OUTPUT)
++ vdev->ioctl_ops = &stf_vid_ioctl_ops_out;
++ else
++ vdev->ioctl_ops = is_mp ? &stf_vid_ioctl_ops_mp : &stf_vid_ioctl_ops;
++ vdev->release = stf_video_release;
++ vdev->v4l2_dev = v4l2_dev;
++ vdev->queue = &video->vb2_q;
++ vdev->lock = &video->lock;
++ //strlcpy(vdev->name, name, sizeof(vdev->name));
++ strscpy(vdev->name, name, sizeof(vdev->name));
++
++ ret = video_register_device(vdev, VFL_TYPE_VIDEO, video->id);
++ if (ret < 0) {
++ st_err(ST_VIDEO,
++ "Failed to register video device: %d\n",
++ ret);
++ goto err_vid_reg;
++ }
++
++ video_set_drvdata(vdev, video);
++ return 0;
++
++err_vid_reg:
++err_vid_init_format:
++ media_entity_cleanup(&vdev->entity);
++ mutex_destroy(&video->lock);
++err_vb2_init:
++ mutex_destroy(&video->q_lock);
++ return ret;
++}
++
++void stf_video_unregister(struct stfcamss_video *video)
++{
++ vb2_video_unregister_device(&video->vdev);
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.h
+@@ -0,0 +1,83 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_VIDEO_H
++#define STF_VIDEO_H
++
++#include <linux/mutex.h>
++#include <media/videobuf2-v4l2.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fh.h>
++#include <media/v4l2-ioctl.h>
++
++#define STFCAMSS_FRAME_MIN_WIDTH 64
++#define STFCAMSS_FRAME_MAX_WIDTH 1920
++#define STFCAMSS_FRAME_MIN_HEIGHT 64
++#define STFCAMSS_FRAME_MAX_HEIGHT 1080
++#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8
++#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128
++#define STFCAMSS_MIN_BUFFERS 2
++
++#define STFCAMSS_MAX_ENTITY_NAME_LEN 27
++
++struct stfcamss_buffer {
++ struct vb2_v4l2_buffer vb;
++ dma_addr_t addr[3];
++ void *vaddr_sc; /* Use for isp sc data */
++ struct list_head queue;
++ int sizeimage;
++};
++
++struct stfcamss_video;
++
++struct stfcamss_video_ops {
++ int (*queue_buffer)(struct stfcamss_video *vid,
++ struct stfcamss_buffer *buf);
++ int (*flush_buffers)(struct stfcamss_video *vid,
++ enum vb2_buffer_state state);
++};
++
++struct fract {
++ u8 numerator;
++ u8 denominator;
++};
++
++struct stfcamss_format_info {
++ u32 code;
++ u32 pixelformat;
++ u8 planes;
++ struct fract hsub[3];
++ struct fract vsub[3];
++ u8 bpp[3];
++};
++
++struct stfcamss_video {
++ struct stfcamss *stfcamss;
++ u8 id;
++ struct vb2_queue vb2_q;
++ struct video_device vdev;
++ struct media_pad pad;
++ struct media_pipeline pipe;
++ struct v4l2_format active_fmt;
++ enum v4l2_buf_type type;
++ const struct stfcamss_video_ops *ops;
++ struct mutex lock;
++ struct mutex q_lock;
++ unsigned int bpl_alignment;
++ const struct stfcamss_format_info *formats;
++ unsigned int nformats;
++ unsigned int is_mp;
++ unsigned int pm_count;
++};
++
++int stf_video_register(struct stfcamss_video *video,
++ struct v4l2_device *v4l2_dev, const char *name, int is_mp);
++
++void stf_video_unregister(struct stfcamss_video *video);
++
++#endif /* STF_VIDEO_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -0,0 +1,1515 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/pm_runtime.h>
++#include <media/v4l2-event.h>
++
++#include "stfcamss.h"
++
++#define vin_line_array(ptr_line) \
++ ((const struct vin_line (*)[]) &(ptr_line[-(ptr_line->id)]))
++
++#define line_to_vin2_dev(ptr_line) \
++ container_of(vin_line_array(ptr_line), struct stf_vin2_dev, line)
++
++#define VIN_FRAME_DROP_MAX_VAL 90
++#define VIN_FRAME_DROP_MIN_VAL 4
++#define VIN_FRAME_PER_SEC_MAX_VAL 90
++
++/* ISP ctrl need 1 sec to let frames become stable. */
++#define VIN_FRAME_DROP_SEC_FOR_ISP_CTRL 1
++
++
++// #define VIN_TWO_BUFFER
++
++static const struct vin2_format vin2_formats_st7110[] = {
++ { MEDIA_BUS_FMT_YUYV8_2X8, 16},
++ { MEDIA_BUS_FMT_RGB565_2X8_LE, 16},
++ { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++ { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++ { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++ { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++ { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++ { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++ { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++ { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++ { MEDIA_BUS_FMT_Y12_1X12, 8},
++ { MEDIA_BUS_FMT_YUV8_1X24, 8},
++ { MEDIA_BUS_FMT_AYUV8_1X32, 32},
++};
++
++static const struct vin2_format isp_formats_st7110_raw[] = {
++ { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++ { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++ { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++ { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++};
++
++static const struct vin2_format isp_formats_st7110_uo[] = {
++ { MEDIA_BUS_FMT_Y12_1X12, 8},
++};
++
++static const struct vin2_format isp_formats_st7110_iti[] = {
++ { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++ { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++ { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++ { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++ { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++ { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++ { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++ { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++ { MEDIA_BUS_FMT_Y12_1X12, 8},
++ { MEDIA_BUS_FMT_YUV8_1X24, 8},
++};
++
++static const struct vin2_format_table vin2_formats_table[] = {
++ /* VIN_LINE_WR */
++ { vin2_formats_st7110, ARRAY_SIZE(vin2_formats_st7110) },
++ /* VIN_LINE_ISP */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++ /* VIN_LINE_ISP_SS0 */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++ /* VIN_LINE_ISP_SS1 */
++ { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++ /* VIN_LINE_ISP_ITIW */
++ { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },
++ /* VIN_LINE_ISP_ITIR */
++ { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },
++ /* VIN_LINE_ISP_RAW */
++ { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },
++ /* VIN_LINE_ISP_SCD_Y */
++ { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },
++};
++
++static void vin_buffer_done(struct vin_line *line, struct vin_params *params);
++static void vin_change_buffer(struct vin_line *line);
++static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output);
++static void vin_output_init_addrs(struct vin_line *line);
++static void vin_init_outputs(struct vin_line *line);
++static struct v4l2_mbus_framefmt *
++__vin_get_format(struct vin_line *line,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which);
++
++static char *get_line_subdevname(int line_id)
++{
++ char *name = NULL;
++
++ switch (line_id) {
++ case VIN_LINE_WR:
++ name = "wr";
++ break;
++ case VIN_LINE_ISP:
++ name = "isp0";
++ break;
++ case VIN_LINE_ISP_SS0:
++ name = "isp0_ss0";
++ break;
++ case VIN_LINE_ISP_SS1:
++ name = "isp0_ss1";
++ break;
++ case VIN_LINE_ISP_ITIW:
++ name = "isp0_itiw";
++ break;
++ case VIN_LINE_ISP_ITIR:
++ name = "isp0_itir";
++ break;
++ case VIN_LINE_ISP_RAW:
++ name = "isp0_raw";
++ break;
++ case VIN_LINE_ISP_SCD_Y:
++ name = "isp0_scd_y";
++ break;
++ default:
++ name = "unknow";
++ break;
++ }
++ return name;
++}
++
++static enum isp_line_id stf_vin_map_isp_line(enum vin_line_id line)
++{
++ enum isp_line_id line_id;
++
++ if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX)) {
++ line_id = line % STF_ISP_LINE_MAX;
++ if (line_id == 0)
++ line_id = STF_ISP_LINE_SRC_SCD_Y;
++ } else
++ line_id = STF_ISP_LINE_INVALID;
++
++ return line_id;
++}
++
++enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def)
++{
++ enum isp_pad_id pad_id;
++
++ if (line == VIN_LINE_WR)
++ pad_id = STF_ISP_PAD_SINK;
++ else if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX))
++ pad_id = stf_vin_map_isp_line(line);
++ else
++ pad_id = def;
++
++ return pad_id;
++}
++
++int stf_vin_subdev_init(struct stfcamss *stfcamss)
++{
++ struct stf_vin_dev *vin;
++ struct device *dev = stfcamss->dev;
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ int i, ret = 0;
++
++ vin_dev->stfcamss = stfcamss;
++ vin_dev->hw_ops = &vin_ops;
++ vin_dev->hw_ops->isr_buffer_done = vin_buffer_done;
++ vin_dev->hw_ops->isr_change_buffer = vin_change_buffer;
++
++ vin = stfcamss->vin;
++ atomic_set(&vin_dev->ref_count, 0);
++
++ ret = devm_request_irq(dev,
++ vin->irq, vin_dev->hw_ops->vin_wr_irq_handler,
++ 0, "vin_axiwr_irq", vin_dev);
++ if (ret) {
++ st_err(ST_VIN, "failed to request irq\n");
++ goto out;
++ }
++
++ ret = devm_request_irq(dev,
++ vin->isp_irq, vin_dev->hw_ops->vin_isp_irq_handler,
++ 0, "vin_isp_irq", vin_dev);
++ if (ret) {
++ st_err(ST_VIN, "failed to request isp irq\n");
++ goto out;
++ }
++
++ st_info(ST_CAMSS, "%s, %d!\n", __func__, __LINE__);
++#ifdef ISP_USE_CSI_AND_SC_DONE_INTERRUPT
++ ret = devm_request_irq(dev,
++ vin->isp_csi_irq, vin_dev->hw_ops->vin_isp_csi_irq_handler,
++ 0, "vin_isp_csi_irq", vin_dev);
++ if (ret) {
++ st_err(ST_VIN, "failed to request isp raw irq\n");
++ goto out;
++ }
++
++ ret = devm_request_irq(dev,
++ vin->isp_scd_irq, vin_dev->hw_ops->vin_isp_scd_irq_handler,
++ 0, "vin_isp_scd_irq", vin_dev);
++ if (ret) {
++ st_err(ST_VIN, "failed to request isp scd irq\n");
++ goto out;
++ }
++#endif
++
++ ret = devm_request_irq(dev,
++ vin->isp_irq_csiline, vin_dev->hw_ops->vin_isp_irq_csiline_handler,
++ 0, "vin_isp_irq_csiline", vin_dev);
++ if (ret) {
++ st_err(ST_VIN, "failed to request isp irq csiline\n");
++ goto out;
++ }
++
++ mutex_init(&vin_dev->power_lock);
++ vin_dev->power_count = 0;
++
++ for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) {
++ struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[i];
++
++ mutex_init(&dummy_buffer->stream_lock);
++ dummy_buffer->nums = i == 0 ? VIN_DUMMY_BUFFER_NUMS : ISP_DUMMY_BUFFER_NUMS;
++ dummy_buffer->stream_count = 0;
++ dummy_buffer->buffer = devm_kzalloc(dev,
++ dummy_buffer->nums * sizeof(struct vin_dummy_buffer), GFP_KERNEL);
++ atomic_set(&dummy_buffer->frame_skip, 0);
++ }
++
++ for (i = VIN_LINE_WR;
++ i < STF_ISP_LINE_MAX + 1; i++) {
++ struct vin_line *l = &vin_dev->line[i];
++ int is_mp;
++
++ is_mp = i == VIN_LINE_WR ? false : true;
++ is_mp = false;
++ if (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIR)
++ l->video_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++ else
++ l->video_out.type = is_mp ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
++ V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ l->video_out.stfcamss = stfcamss;
++ l->id = i;
++ l->sdev_type = VIN_DEV_TYPE;
++ l->formats = vin2_formats_table[i].fmts;
++ l->nformats = vin2_formats_table[i].nfmts;
++ spin_lock_init(&l->output_lock);
++
++ mutex_init(&l->stream_lock);
++ l->stream_count = 0;
++ mutex_init(&l->power_lock);
++ l->power_count = 0;
++ }
++
++ return 0;
++out:
++ return ret;
++}
++
++static int vin_set_power(struct v4l2_subdev *sd, int on)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++ mutex_lock(&line->power_lock);
++ if (on) {
++ if (line->power_count == 0)
++ vin_init_outputs(line);
++ line->power_count++;
++ } else {
++ if (line->power_count == 0) {
++ st_err(ST_VIN,
++ "line power off on power_count == 0\n");
++ goto exit_line;
++ }
++ line->power_count--;
++ }
++exit_line:
++ mutex_unlock(&line->power_lock);
++
++ mutex_lock(&vin_dev->power_lock);
++ if (on) {
++ if (vin_dev->power_count == 0) {
++ pm_runtime_get_sync(stfcamss->dev);
++ vin_dev->hw_ops->vin_clk_enable(vin_dev);
++ vin_dev->hw_ops->vin_config_set(vin_dev);
++ }
++ vin_dev->power_count++;
++ } else {
++ if (vin_dev->power_count == 0) {
++ st_err(ST_VIN,
++ "vin_dev power off on power_count == 0\n");
++ goto exit;
++ }
++ if (vin_dev->power_count == 1) {
++ vin_dev->hw_ops->vin_clk_disable(vin_dev);
++ pm_runtime_put_sync(stfcamss->dev);
++ }
++ vin_dev->power_count--;
++ }
++exit:
++
++ mutex_unlock(&vin_dev->power_lock);
++
++ return 0;
++}
++
++static unsigned int get_frame_skip(struct vin_line *line)
++{
++ unsigned int frame_skip = 0;
++ unsigned int isp_ctrl_skip_frames = 0;
++ struct media_entity *sensor;
++ struct v4l2_subdev_frame_interval fi;
++
++ sensor = stfcamss_find_sensor(&line->subdev.entity);
++ if (sensor) {
++ int fps = 0;
++ struct v4l2_subdev *subdev =
++ media_entity_to_v4l2_subdev(sensor);
++
++ if (subdev->ops->video->g_frame_interval) {
++ if (!subdev->ops->video->g_frame_interval(subdev, &fi))
++ fps = fi.interval.denominator;
++
++ if (fps > 0 && fps <= 90)
++ isp_ctrl_skip_frames = fps * VIN_FRAME_DROP_SEC_FOR_ISP_CTRL;
++ }
++ if (!fps)
++ st_debug(ST_VIN, "%s, Failed to get sensor fps !\n", __func__);
++
++ if (isp_ctrl_skip_frames <= VIN_FRAME_DROP_MIN_VAL)
++ isp_ctrl_skip_frames = VIN_FRAME_DROP_MIN_VAL;
++
++ v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
++
++ frame_skip += isp_ctrl_skip_frames;
++
++ if (frame_skip > VIN_FRAME_DROP_MAX_VAL)
++ frame_skip = VIN_FRAME_DROP_MAX_VAL;
++ st_debug(ST_VIN, "%s, frame_skip %d\n", __func__, frame_skip);
++ }
++
++ return frame_skip;
++}
++
++static void vin_buf_l2cache_flush(struct vin_output *output)
++{
++ struct stfcamss_buffer *buffer = NULL;
++
++ if (!list_empty(&output->pending_bufs)) {
++ list_for_each_entry(buffer, &output->pending_bufs, queue) {
++ sifive_l2_flush64_range(buffer->addr[0], buffer->sizeimage);
++ }
++ }
++}
++
++static int vin_enable_output(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++ unsigned long flags;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ output->state = VIN_OUTPUT_IDLE;
++
++ vin_buf_l2cache_flush(output);
++
++ output->buf[0] = vin_buf_get_pending(output);
++#ifdef VIN_TWO_BUFFER
++ if (line->id == VIN_LINE_WR)
++ output->buf[1] = vin_buf_get_pending(output);
++#endif
++ if (!output->buf[0] && output->buf[1]) {
++ output->buf[0] = output->buf[1];
++ output->buf[1] = NULL;
++ }
++
++ if (output->buf[0])
++ output->state = VIN_OUTPUT_SINGLE;
++
++#ifdef VIN_TWO_BUFFER
++ if (output->buf[1] && line->id == VIN_LINE_WR)
++ output->state = VIN_OUTPUT_CONTINUOUS;
++#endif
++ output->sequence = 0;
++
++ vin_output_init_addrs(line);
++ spin_unlock_irqrestore(&line->output_lock, flags);
++ return 0;
++}
++
++static int vin_disable_output(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++ unsigned long flags;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ output->state = VIN_OUTPUT_OFF;
++
++ spin_unlock_irqrestore(&line->output_lock, flags);
++ return 0;
++}
++
++static u32 line_to_dummy_module(struct vin_line *line)
++{
++ u32 dummy_module = 0;
++
++ switch (line->id) {
++ case VIN_LINE_WR:
++ dummy_module = STF_DUMMY_VIN;
++ break;
++ case VIN_LINE_ISP:
++ case VIN_LINE_ISP_SS0:
++ case VIN_LINE_ISP_SS1:
++ case VIN_LINE_ISP_ITIW:
++ case VIN_LINE_ISP_ITIR:
++ case VIN_LINE_ISP_RAW:
++ case VIN_LINE_ISP_SCD_Y:
++ dummy_module = STF_DUMMY_ISP;
++ break;
++ default:
++ dummy_module = STF_DUMMY_VIN;
++ break;
++ }
++
++ return dummy_module;
++}
++
++static int vin_alloc_dummy_buffer(struct stf_vin2_dev *vin_dev,
++ struct v4l2_mbus_framefmt *fmt, int dummy_module)
++{
++ struct device *dev = vin_dev->stfcamss->dev;
++ struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++ struct vin_dummy_buffer *buffer = NULL;
++ int ret = 0, i;
++ u32 aligns;
++
++ for (i = 0; i < dummy_buffer->nums; i++) {
++ buffer = &vin_dev->dummy_buffer[dummy_module].buffer[i];
++ buffer->width = fmt->width;
++ buffer->height = fmt->height;
++ buffer->mcode = fmt->code;
++ if (i == STF_VIN_PAD_SINK) {
++ aligns = ALIGN(fmt->width * 4, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++ buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height);
++ } else if (i == STF_ISP_PAD_SRC
++ || i == STF_ISP_PAD_SRC_SS0
++ || i == STF_ISP_PAD_SRC_SS1) {
++ aligns = ALIGN(fmt->width, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++ buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height * 3 / 2);
++ } else if (i == STF_ISP_PAD_SRC_ITIW
++ || i == STF_ISP_PAD_SRC_ITIR) {
++ aligns = ALIGN(fmt->width, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++ buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height * 3);
++ } else if (i == STF_ISP_PAD_SRC_RAW) {
++ aligns = ALIGN(fmt->width * ISP_RAW_DATA_BITS / 8,
++ STFCAMSS_FRAME_WIDTH_ALIGN_128);
++ buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height);
++ } else if (i == STF_ISP_PAD_SRC_SCD_Y)
++ buffer->buffer_size = PAGE_ALIGN(ISP_SCD_Y_BUFFER_SIZE);
++ else
++ continue;
++
++ buffer->vaddr = dma_alloc_coherent(dev, buffer->buffer_size,
++ &buffer->paddr[0], GFP_DMA | GFP_KERNEL);
++
++ if (buffer->vaddr) {
++ if (i == STF_ISP_PAD_SRC
++ || i == STF_ISP_PAD_SRC_SS0
++ || i == STF_ISP_PAD_SRC_SS1
++ || i == STF_ISP_PAD_SRC_ITIW
++ || i == STF_ISP_PAD_SRC_ITIR)
++ buffer->paddr[1] = (dma_addr_t)(buffer->paddr[0] +
++ aligns * fmt->height);
++ else if (i == STF_ISP_PAD_SRC_SCD_Y)
++ buffer->paddr[1] = (dma_addr_t)(buffer->paddr[0] +
++ ISP_YHIST_BUFFER_SIZE);
++ else
++ st_debug(ST_VIN, "signal plane\n");
++ }
++ {
++ char szPadName[][32] = {
++ "VIN_PAD_SINK",
++ "ISP_PAD_SRC",
++ "ISP_PAD_SRC_SS0",
++ "ISP_PAD_SRC_SS1",
++ "ISP_PAD_SRC_ITIW",
++ "ISP_PAD_SRC_ITIR",
++ "ISP_PAD_SRC_RAW",
++ "ISP_PAD_SRC_SCD_Y",
++ "Unknown Pad"
++ };
++
++ st_debug(ST_VIN, "%s: i = %d(%s) addr[0] = %llx, addr[1] = %llx, size = %u bytes\n",
++ __func__,
++ i,
++ szPadName[i],
++ buffer->paddr[0],
++ buffer->paddr[1],
++ buffer->buffer_size
++ );
++ }
++ }
++
++ return ret;
++}
++
++static void vin_free_dummy_buffer(struct stf_vin2_dev *vin_dev, int dummy_module)
++{
++ struct device *dev = vin_dev->stfcamss->dev;
++ struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++ struct vin_dummy_buffer *buffer = NULL;
++ int i;
++
++ for (i = 0; i < dummy_buffer->nums; i++) {
++ buffer = &dummy_buffer->buffer[i];
++ if (buffer->vaddr)
++ dma_free_coherent(dev, buffer->buffer_size,
++ buffer->vaddr, buffer->paddr[0]);
++ memset(buffer, 0, sizeof(struct vin_dummy_buffer));
++ }
++}
++
++static void vin_set_dummy_buffer(struct vin_line *line, u32 pad)
++{
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ int dummy_module = line_to_dummy_module(line);
++ struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++ struct vin_dummy_buffer *buffer = NULL;
++
++ switch (pad) {
++ case STF_VIN_PAD_SINK:
++ if (line->id == VIN_LINE_WR) {
++ buffer = &dummy_buffer->buffer[STF_VIN_PAD_SINK];
++ vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, buffer->paddr[0]);
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, buffer->paddr[0]);
++ } else {
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC];
++ vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0];
++ vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1];
++ vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIW];
++ vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIR];
++ vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW];
++ vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, buffer->paddr[0]);
++
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SCD_Y];
++ vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1], AWB_TYPE);
++ }
++ break;
++ case STF_ISP_PAD_SRC:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC];
++ vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++ break;
++ case STF_ISP_PAD_SRC_SS0:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0];
++ vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++ break;
++ case STF_ISP_PAD_SRC_SS1:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1];
++ vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++ break;
++ case STF_ISP_PAD_SRC_ITIW:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIW];
++ vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++ break;
++ case STF_ISP_PAD_SRC_ITIR:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIR];
++ vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1]);
++ break;
++ case STF_ISP_PAD_SRC_RAW:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW];
++ vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, buffer->paddr[0]);
++ break;
++ case STF_ISP_PAD_SRC_SCD_Y:
++ buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SCD_Y];
++ vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++ buffer->paddr[0], buffer->paddr[1], AWB_TYPE);
++ break;
++ default:
++ break;
++ }
++}
++
++static int vin_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ int dummy_module = line_to_dummy_module(line);
++ struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++ struct v4l2_mbus_framefmt *fmt;
++
++ st_debug(ST_VIN, "%s, %d\n", __func__, __LINE__);
++ fmt = __vin_get_format(line, NULL, STF_VIN_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE);
++ mutex_lock(&dummy_buffer->stream_lock);
++ if (enable) {
++ if (dummy_buffer->stream_count == 0) {
++ vin_alloc_dummy_buffer(vin_dev, fmt, dummy_module);
++ vin_set_dummy_buffer(line, STF_VIN_PAD_SINK);
++ atomic_set(&dummy_buffer->frame_skip, get_frame_skip(line));
++ }
++ dummy_buffer->stream_count++;
++ } else {
++ if (dummy_buffer->stream_count == 1) {
++ vin_free_dummy_buffer(vin_dev, dummy_module);
++ // set buffer addr to zero
++ vin_set_dummy_buffer(line, STF_VIN_PAD_SINK);
++ } else
++ vin_set_dummy_buffer(line,
++ stf_vin_map_isp_pad(line->id, STF_ISP_PAD_SINK));
++
++ dummy_buffer->stream_count--;
++ }
++ mutex_unlock(&dummy_buffer->stream_lock);
++
++ mutex_lock(&line->stream_lock);
++ if (enable) {
++ if (line->stream_count == 0) {
++ if (line->id == VIN_LINE_WR) {
++ vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1);
++ vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 1);
++ }
++ }
++ line->stream_count++;
++ } else {
++ if (line->stream_count == 1) {
++ if (line->id == VIN_LINE_WR) {
++ vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0);
++ vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 0);
++ }
++ }
++ line->stream_count--;
++ }
++ mutex_unlock(&line->stream_lock);
++
++ if (enable)
++ vin_enable_output(line);
++ else
++ vin_disable_output(line);
++
++ return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__vin_get_format(struct vin_line *line,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ enum v4l2_subdev_format_whence which)
++{
++ if (which == V4L2_SUBDEV_FORMAT_TRY)
++ return v4l2_subdev_get_try_format(&line->subdev, state, pad);
++ return &line->fmt[pad];
++}
++
++static void vin_try_format(struct vin_line *line,
++ struct v4l2_subdev_state *state,
++ unsigned int pad,
++ struct v4l2_mbus_framefmt *fmt,
++ enum v4l2_subdev_format_whence which)
++{
++ unsigned int i;
++
++ switch (pad) {
++ case STF_VIN_PAD_SINK:
++ /* Set format on sink pad */
++
++ for (i = 0; i < line->nformats; i++)
++ if (fmt->code == line->formats[i].code)
++ break;
++
++ /* If not found, use UYVY as default */
++ if (i >= line->nformats)
++ fmt->code = line->formats[0].code;
++
++ fmt->width = clamp_t(u32,
++ fmt->width, STFCAMSS_FRAME_MIN_WIDTH, STFCAMSS_FRAME_MAX_WIDTH);
++ fmt->height = clamp_t(u32,
++ fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, STFCAMSS_FRAME_MAX_HEIGHT);
++
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->flags = 0;
++
++ break;
++
++ case STF_VIN_PAD_SRC:
++ /* Set and return a format same as sink pad */
++ *fmt = *__vin_get_format(line, state, STF_VIN_PAD_SINK, which);
++ break;
++ }
++
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++}
++
++static int vin_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++
++ if (code->index >= line->nformats)
++ return -EINVAL;
++ if (code->pad == STF_VIN_PAD_SINK) {
++ code->code = line->formats[code->index].code;
++ } else {
++ struct v4l2_mbus_framefmt *sink_fmt;
++
++ sink_fmt = __vin_get_format(line, state, STF_VIN_PAD_SINK,
++ code->which);
++
++ code->code = sink_fmt->code;
++ if (!code->code)
++ return -EINVAL;
++ }
++ code->flags = 0;
++
++ return 0;
++}
++
++static int vin_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt format;
++
++ if (fse->index != 0)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = 1;
++ format.height = 1;
++ vin_try_format(line, state, fse->pad, &format, fse->which);
++ fse->min_width = format.width;
++ fse->min_height = format.height;
++
++ if (format.code != fse->code)
++ return -EINVAL;
++
++ format.code = fse->code;
++ format.width = -1;
++ format.height = -1;
++ vin_try_format(line, state, fse->pad, &format, fse->which);
++ fse->max_width = format.width;
++ fse->max_height = format.height;
++
++ return 0;
++}
++
++static int vin_get_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ format = __vin_get_format(line, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ fmt->format = *format;
++
++ return 0;
++}
++
++static int vin_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct vin_line *line = v4l2_get_subdevdata(sd);
++ struct v4l2_mbus_framefmt *format;
++
++ st_debug(ST_VIDEO, "%s, pad %d, fmt code %x\n",
++ __func__, fmt->pad, fmt->format.code);
++
++ format = __vin_get_format(line, state, fmt->pad, fmt->which);
++ if (format == NULL)
++ return -EINVAL;
++
++ mutex_lock(&line->stream_lock);
++ if (line->stream_count) {
++ fmt->format = *format;
++ mutex_unlock(&line->stream_lock);
++ goto out;
++ } else {
++ vin_try_format(line, state, fmt->pad, &fmt->format, fmt->which);
++ *format = fmt->format;
++ }
++ mutex_unlock(&line->stream_lock);
++
++ if (fmt->pad == STF_VIN_PAD_SINK) {
++ /* Propagate the format from sink to source */
++ format = __vin_get_format(line, state, STF_VIN_PAD_SRC,
++ fmt->which);
++
++ *format = fmt->format;
++ vin_try_format(line, state, STF_VIN_PAD_SRC, format,
++ fmt->which);
++ }
++
++out:
++ return 0;
++}
++
++static int vin_init_formats(struct v4l2_subdev *sd,
++ struct v4l2_subdev_fh *fh)
++{
++ struct v4l2_subdev_format format = {
++ .pad = STF_VIN_PAD_SINK,
++ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++ V4L2_SUBDEV_FORMAT_ACTIVE,
++ .format = {
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .width = 1920,
++ .height = 1080
++ }
++ };
++
++ return vin_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static void vin_output_init_addrs(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ dma_addr_t ping_addr;
++ dma_addr_t pong_addr;
++ dma_addr_t y_addr, uv_addr;
++
++ output->active_buf = 0;
++
++ if (output->buf[0]) {
++ ping_addr = output->buf[0]->addr[0];
++ y_addr = output->buf[0]->addr[0];
++ uv_addr = output->buf[0]->addr[1];
++ } else
++ return;
++
++ if (output->buf[1])
++ pong_addr = output->buf[1]->addr[0];
++ else
++ pong_addr = ping_addr;
++
++ switch (stf_vin_map_isp_line(line->id)) {
++ case STF_ISP_LINE_SRC:
++ vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++ y_addr, uv_addr);
++ break;
++ case STF_ISP_LINE_SRC_SS0:
++ vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++ y_addr, uv_addr);
++ break;
++ case STF_ISP_LINE_SRC_SS1:
++ vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++ y_addr, uv_addr);
++ break;
++ case STF_ISP_LINE_SRC_ITIW:
++ vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++ y_addr, uv_addr);
++ break;
++ case STF_ISP_LINE_SRC_ITIR:
++ vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++ y_addr, uv_addr);
++ break;
++ case STF_ISP_LINE_SRC_RAW:
++ vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, y_addr);
++ break;
++ case STF_ISP_LINE_SRC_SCD_Y:
++ output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
++ vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++ y_addr, uv_addr, AWB_TYPE);
++ break;
++ default:
++ if (line->id == VIN_LINE_WR) {
++ vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, ping_addr);
++#ifdef VIN_TWO_BUFFER
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, pong_addr);
++#else
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, ping_addr);
++#endif
++ }
++ break;
++ }
++}
++
++static void vin_init_outputs(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++
++ output->state = VIN_OUTPUT_OFF;
++ output->buf[0] = NULL;
++ output->buf[1] = NULL;
++ output->active_buf = 0;
++ INIT_LIST_HEAD(&output->pending_bufs);
++ INIT_LIST_HEAD(&output->ready_bufs);
++}
++
++static void vin_buf_add_ready(struct vin_output *output,
++ struct stfcamss_buffer *buffer)
++{
++ INIT_LIST_HEAD(&buffer->queue);
++ list_add_tail(&buffer->queue, &output->ready_bufs);
++}
++
++static struct stfcamss_buffer *vin_buf_get_ready(struct vin_output *output)
++{
++ struct stfcamss_buffer *buffer = NULL;
++
++ if (!list_empty(&output->ready_bufs)) {
++ buffer = list_first_entry(&output->ready_bufs,
++ struct stfcamss_buffer,
++ queue);
++ list_del(&buffer->queue);
++ }
++
++ return buffer;
++}
++
++static void vin_buf_add_pending(struct vin_output *output,
++ struct stfcamss_buffer *buffer)
++{
++ INIT_LIST_HEAD(&buffer->queue);
++ list_add_tail(&buffer->queue, &output->pending_bufs);
++}
++
++static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output)
++{
++ struct stfcamss_buffer *buffer = NULL;
++
++ if (!list_empty(&output->pending_bufs)) {
++ buffer = list_first_entry(&output->pending_bufs,
++ struct stfcamss_buffer,
++ queue);
++ list_del(&buffer->queue);
++ }
++
++ return buffer;
++}
++
++#ifdef UNUSED_CODE
++static void vin_output_checkpending(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++
++ if (output->state == VIN_OUTPUT_STOPPING) {
++ /* Release last buffer when hw is idle */
++ if (output->last_buffer) {
++ // vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
++ // VB2_BUF_STATE_DONE);
++ vin_buf_add_pending(output, output->last_buffer);
++ output->last_buffer = NULL;
++ }
++ output->state = VIN_OUTPUT_IDLE;
++
++ /* Buffers received in stopping state are queued in */
++ /* dma pending queue, start next capture here */
++ output->buf[0] = vin_buf_get_pending(output);
++#ifdef VIN_TWO_BUFFER
++ if (line->id == VIN_LINE_WR)
++ output->buf[1] = vin_buf_get_pending(output);
++#endif
++
++ if (!output->buf[0] && output->buf[1]) {
++ output->buf[0] = output->buf[1];
++ output->buf[1] = NULL;
++ }
++
++ if (output->buf[0])
++ output->state = VIN_OUTPUT_SINGLE;
++
++#ifdef VIN_TWO_BUFFER
++ if (output->buf[1] && line->id == VIN_LINE_WR)
++ output->state = VIN_OUTPUT_CONTINUOUS;
++#endif
++ vin_output_init_addrs(line);
++ }
++}
++#endif
++
++static void vin_buf_update_on_last(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++
++ switch (output->state) {
++ case VIN_OUTPUT_CONTINUOUS:
++ output->state = VIN_OUTPUT_SINGLE;
++ output->active_buf = !output->active_buf;
++ break;
++ case VIN_OUTPUT_SINGLE:
++ output->state = VIN_OUTPUT_STOPPING;
++ break;
++ default:
++ st_err_ratelimited(ST_VIN,
++ "Last buff in wrong state! %d\n",
++ output->state);
++ break;
++ }
++}
++
++static void vin_buf_update_on_next(struct vin_line *line)
++{
++ struct vin_output *output = &line->output;
++
++ switch (output->state) {
++ case VIN_OUTPUT_CONTINUOUS:
++ output->active_buf = !output->active_buf;
++ break;
++ case VIN_OUTPUT_SINGLE:
++ default:
++#ifdef VIN_TWO_BUFFER
++ if (line->id == VIN_LINE_WR)
++ st_err_ratelimited(ST_VIN,
++ "Next buf in wrong state! %d\n",
++ output->state);
++#endif
++ break;
++ }
++}
++
++static void vin_buf_update_on_new(struct vin_line *line,
++ struct vin_output *output,
++ struct stfcamss_buffer *new_buf)
++{
++#ifdef VIN_TWO_BUFFER
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ int inactive_idx;
++#endif
++
++ switch (output->state) {
++ case VIN_OUTPUT_SINGLE:
++#ifdef VIN_TWO_BUFFER
++ int inactive_idx = !output->active_buf;
++
++ if (!output->buf[inactive_idx] && line->id == VIN_LINE_WR) {
++ output->buf[inactive_idx] = new_buf;
++ if (inactive_idx)
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++ output->buf[1]->addr[0]);
++ else
++ vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++ output->buf[0]->addr[0]);
++ output->state = VIN_OUTPUT_CONTINUOUS;
++
++ } else {
++ vin_buf_add_pending(output, new_buf);
++ if (line->id == VIN_LINE_WR)
++ st_warn(ST_VIN, "Inactive buffer is busy\n");
++ }
++#else
++ vin_buf_add_pending(output, new_buf);
++#endif
++ break;
++ case VIN_OUTPUT_IDLE:
++ st_warn(ST_VIN, "Output idle buffer set!\n");
++ if (!output->buf[0]) {
++ output->buf[0] = new_buf;
++ vin_output_init_addrs(line);
++ output->state = VIN_OUTPUT_SINGLE;
++ } else {
++ vin_buf_add_pending(output, new_buf);
++ st_warn(ST_VIN, "Output idle with buffer set!\n");
++ }
++ break;
++ case VIN_OUTPUT_STOPPING:
++ if (output->last_buffer) {
++ output->buf[output->active_buf] = output->last_buffer;
++ output->last_buffer = NULL;
++ } else
++ st_err(ST_VIN, "stop state lost lastbuffer!\n");
++ output->state = VIN_OUTPUT_SINGLE;
++ // vin_output_checkpending(line);
++ vin_buf_add_pending(output, new_buf);
++ break;
++ case VIN_OUTPUT_CONTINUOUS:
++ default:
++ vin_buf_add_pending(output, new_buf);
++ break;
++ }
++}
++
++static void vin_buf_flush(struct vin_output *output,
++ enum vb2_buffer_state state)
++{
++ struct stfcamss_buffer *buf;
++ struct stfcamss_buffer *t;
++
++ list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
++ vb2_buffer_done(&buf->vb.vb2_buf, state);
++ list_del(&buf->queue);
++ }
++ list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) {
++ vb2_buffer_done(&buf->vb.vb2_buf, state);
++ list_del(&buf->queue);
++ }
++}
++
++static void vin_buffer_done(struct vin_line *line, struct vin_params *params)
++{
++ struct stfcamss_buffer *ready_buf;
++ struct vin_output *output = &line->output;
++ unsigned long flags;
++ u64 ts = ktime_get_ns();
++ struct v4l2_event event = {
++ .type = V4L2_EVENT_FRAME_SYNC,
++ };
++
++ if (output->state == VIN_OUTPUT_OFF
++ || output->state == VIN_OUTPUT_RESERVED)
++ return;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ while ((ready_buf = vin_buf_get_ready(output))) {
++ if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++ event.u.frame_sync.frame_sequence = output->sequence;
++ v4l2_event_queue(line->subdev.devnode, &event);
++ }
++
++ ready_buf->vb.vb2_buf.timestamp = ts;
++ ready_buf->vb.sequence = output->sequence++;
++
++ /* The stf_isp_ctrl currently buffered with mmap,
++ * which will not update cache by default.
++ * Flush L2 cache to make sure data is updated.
++ */
++ if (ready_buf->vb.vb2_buf.memory == VB2_MEMORY_MMAP)
++ sifive_l2_flush64_range(ready_buf->addr[0], ready_buf->sizeimage);
++
++ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
++ }
++
++ spin_unlock_irqrestore(&line->output_lock, flags);
++}
++
++static void vin_change_buffer(struct vin_line *line)
++{
++ struct stfcamss_buffer *ready_buf;
++ struct vin_output *output = &line->output;
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ dma_addr_t *new_addr;
++ unsigned long flags;
++ u32 active_index;
++ int scd_type;
++
++ if (output->state == VIN_OUTPUT_OFF
++ || output->state == VIN_OUTPUT_STOPPING
++ || output->state == VIN_OUTPUT_RESERVED
++ || output->state == VIN_OUTPUT_IDLE)
++ return;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ active_index = output->active_buf;
++
++ ready_buf = output->buf[active_index];
++ if (!ready_buf) {
++ st_err_ratelimited(ST_VIN,
++ "Missing ready buf %d %d!\n",
++ active_index, output->state);
++ active_index = !active_index;
++ ready_buf = output->buf[active_index];
++ if (!ready_buf) {
++ st_err_ratelimited(ST_VIN,
++ "Missing ready buf 2 %d %d!\n",
++ active_index, output->state);
++ goto out_unlock;
++ }
++ }
++
++ /* Get next buffer */
++ output->buf[active_index] = vin_buf_get_pending(output);
++ if (!output->buf[active_index]) {
++ /* No next buffer - set same address */
++ new_addr = ready_buf->addr;
++ vin_buf_update_on_last(line);
++ } else {
++ new_addr = output->buf[active_index]->addr;
++ vin_buf_update_on_next(line);
++ }
++
++ if (output->state == VIN_OUTPUT_STOPPING)
++ output->last_buffer = ready_buf;
++ else {
++ switch (stf_vin_map_isp_line(line->id)) {
++ case STF_ISP_LINE_SRC:
++ vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++ new_addr[0], new_addr[1]);
++ break;
++ case STF_ISP_LINE_SRC_SS0:
++ vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++ new_addr[0], new_addr[1]);
++ break;
++ case STF_ISP_LINE_SRC_SS1:
++ vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++ new_addr[0], new_addr[1]);
++ break;
++ case STF_ISP_LINE_SRC_ITIW:
++ vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++ new_addr[0], new_addr[1]);
++ break;
++ case STF_ISP_LINE_SRC_ITIR:
++ vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++ new_addr[0], new_addr[1]);
++ break;
++ case STF_ISP_LINE_SRC_RAW:
++ vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, new_addr[0]);
++ break;
++ case STF_ISP_LINE_SRC_SCD_Y:
++ scd_type = vin_dev->hw_ops->vin_isp_get_scd_type(vin_dev);
++ ready_buf->vb.flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
++ if (scd_type == AWB_TYPE)
++ ready_buf->vb.flags |= V4L2_BUF_FLAG_PFRAME;
++ else
++ ready_buf->vb.flags |= V4L2_BUF_FLAG_BFRAME;
++ if (!output->frame_skip) {
++ output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
++ scd_type = scd_type == AWB_TYPE ? OECF_TYPE : AWB_TYPE;
++ } else {
++ output->frame_skip--;
++ scd_type = scd_type == AWB_TYPE ? AWB_TYPE : OECF_TYPE;
++ }
++ vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++ new_addr[0], new_addr[1], scd_type);
++ break;
++ default:
++ if (line->id == VIN_LINE_WR) {
++#ifdef VIN_TWO_BUFFER
++ if (active_index)
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++ new_addr[0]);
++ else
++ vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++ new_addr[0]);
++#else
++ vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++ new_addr[0]);
++ vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++ new_addr[0]);
++#endif
++ }
++ break;
++ }
++
++ vin_buf_add_ready(output, ready_buf);
++ }
++
++ spin_unlock_irqrestore(&line->output_lock, flags);
++ return;
++
++out_unlock:
++ spin_unlock_irqrestore(&line->output_lock, flags);
++}
++
++static int vin_queue_buffer(struct stfcamss_video *vid,
++ struct stfcamss_buffer *buf)
++{
++ struct vin_line *line = container_of(vid, struct vin_line, video_out);
++ struct vin_output *output;
++ unsigned long flags;
++
++
++ output = &line->output;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ vin_buf_update_on_new(line, output, buf);
++
++ spin_unlock_irqrestore(&line->output_lock, flags);
++
++ return 0;
++}
++
++static int vin_flush_buffers(struct stfcamss_video *vid,
++ enum vb2_buffer_state state)
++{
++ struct vin_line *line = container_of(vid, struct vin_line, video_out);
++ struct vin_output *output = &line->output;
++ unsigned long flags;
++
++ spin_lock_irqsave(&line->output_lock, flags);
++
++ vin_buf_flush(output, state);
++ if (output->buf[0])
++ vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
++
++ if (output->buf[1])
++ vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
++
++ if (output->last_buffer) {
++ vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
++ output->last_buffer = NULL;
++ }
++ output->buf[0] = output->buf[1] = NULL;
++
++ spin_unlock_irqrestore(&line->output_lock, flags);
++ return 0;
++}
++
++static int vin_link_setup(struct media_entity *entity,
++ const struct media_pad *local,
++ const struct media_pad *remote, u32 flags)
++{
++ if (flags & MEDIA_LNK_FL_ENABLED)
++ if (media_pad_remote_pad_first(local))
++ return -EBUSY;
++ return 0;
++}
++
++static int stf_vin_subscribe_event(struct v4l2_subdev *sd,
++ struct v4l2_fh *fh,
++ struct v4l2_event_subscription *sub)
++{
++ switch (sub->type) {
++ case V4L2_EVENT_FRAME_SYNC:
++ return v4l2_event_subscribe(fh, sub, 0, NULL);
++ default:
++ st_debug(ST_VIN, "unsupport subscribe_event\n");
++ return -EINVAL;
++ }
++}
++
++static const struct v4l2_subdev_core_ops vin_core_ops = {
++ .s_power = vin_set_power,
++ .subscribe_event = stf_vin_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops vin_video_ops = {
++ .s_stream = vin_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops vin_pad_ops = {
++ .enum_mbus_code = vin_enum_mbus_code,
++ .enum_frame_size = vin_enum_frame_size,
++ .get_fmt = vin_get_format,
++ .set_fmt = vin_set_format,
++};
++
++static const struct v4l2_subdev_ops vin_v4l2_ops = {
++ .core = &vin_core_ops,
++ .video = &vin_video_ops,
++ .pad = &vin_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops vin_v4l2_internal_ops = {
++ .open = vin_init_formats,
++};
++
++static const struct stfcamss_video_ops stfcamss_vin_video_ops = {
++ .queue_buffer = vin_queue_buffer,
++ .flush_buffers = vin_flush_buffers,
++};
++
++static const struct media_entity_operations vin_media_ops = {
++ .link_setup = vin_link_setup,
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_vin_register(struct stf_vin2_dev *vin_dev, struct v4l2_device *v4l2_dev)
++{
++ struct v4l2_subdev *sd;
++ struct stfcamss_video *video_out;
++ struct media_pad *pads;
++ int ret;
++ int i;
++
++ for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) {
++ char name[32];
++ char *sub_name = get_line_subdevname(i);
++ int is_mp;
++
++#ifdef STF_CAMSS_SKIP_ITI
++ if ((stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIW) ||
++ (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIR))
++ continue;
++#endif
++ is_mp = (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC) ? true : false;
++ is_mp = false;
++ sd = &vin_dev->line[i].subdev;
++ pads = vin_dev->line[i].pads;
++ video_out = &vin_dev->line[i].video_out;
++ video_out->id = i;
++
++ v4l2_subdev_init(sd, &vin_v4l2_ops);
++ sd->internal_ops = &vin_v4l2_internal_ops;
++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
++ STF_VIN_NAME, 0, sub_name);
++ v4l2_set_subdevdata(sd, &vin_dev->line[i]);
++
++ ret = vin_init_formats(sd, NULL);
++ if (ret < 0) {
++ st_err(ST_VIN, "Failed to init format: %d\n", ret);
++ goto err_init;
++ }
++
++ pads[STF_VIN_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++ pads[STF_VIN_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++ sd->entity.function =
++ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->entity.ops = &vin_media_ops;
++ ret = media_entity_pads_init(&sd->entity,
++ STF_VIN_PADS_NUM, pads);
++ if (ret < 0) {
++ st_err(ST_VIN, "Failed to init media entity: %d\n", ret);
++ goto err_init;
++ }
++
++ ret = v4l2_device_register_subdev(v4l2_dev, sd);
++ if (ret < 0) {
++ st_err(ST_VIN, "Failed to register subdev: %d\n", ret);
++ goto err_reg_subdev;
++ }
++
++ video_out->ops = &stfcamss_vin_video_ops;
++ video_out->bpl_alignment = 16 * 8;
++
++ snprintf(name, ARRAY_SIZE(name), "%s_%s%d",
++ sd->name, "video", i);
++ ret = stf_video_register(video_out, v4l2_dev, name, is_mp);
++ if (ret < 0) {
++ st_err(ST_VIN, "Failed to register video node: %d\n",
++ ret);
++ goto err_vid_reg;
++ }
++
++ ret = media_create_pad_link(
++ &sd->entity, STF_VIN_PAD_SRC,
++ &video_out->vdev.entity, 0,
++ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++ if (ret < 0) {
++ st_err(ST_VIN, "Failed to link %s->%s entities: %d\n",
++ sd->entity.name, video_out->vdev.entity.name,
++ ret);
++ goto err_create_link;
++ }
++ }
++
++ return 0;
++
++err_create_link:
++ stf_video_unregister(video_out);
++err_vid_reg:
++ v4l2_device_unregister_subdev(sd);
++err_reg_subdev:
++ media_entity_cleanup(&sd->entity);
++err_init:
++ for (i--; i >= 0; i--) {
++ sd = &vin_dev->line[i].subdev;
++ video_out = &vin_dev->line[i].video_out;
++
++ stf_video_unregister(video_out);
++ v4l2_device_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ }
++ return ret;
++}
++
++int stf_vin_unregister(struct stf_vin2_dev *vin_dev)
++{
++ struct v4l2_subdev *sd;
++ struct stfcamss_video *video_out;
++ int i;
++
++ mutex_destroy(&vin_dev->power_lock);
++ for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++)
++ mutex_destroy(&vin_dev->dummy_buffer[i].stream_lock);
++
++ for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) {
++ sd = &vin_dev->line[i].subdev;
++ video_out = &vin_dev->line[i].video_out;
++
++ stf_video_unregister(video_out);
++ v4l2_device_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ mutex_destroy(&vin_dev->line[i].stream_lock);
++ mutex_destroy(&vin_dev->line[i].power_lock);
++ }
++ return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.h
+@@ -0,0 +1,182 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_VIN_H
++#define STF_VIN_H
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++#include <linux/spinlock_types.h>
++#include <video/stf-vin.h>
++#include <linux/platform_device.h>
++
++#include "stf_video.h"
++
++#define STF_VIN_NAME "stf_vin"
++
++#define STF_VIN_PAD_SINK 0
++#define STF_VIN_PAD_SRC 1
++#define STF_VIN_PADS_NUM 2
++
++struct vin2_format {
++ u32 code;
++ u8 bpp;
++};
++
++struct vin2_format_table {
++ const struct vin2_format *fmts;
++ int nfmts;
++};
++
++enum vin_output_state {
++ VIN_OUTPUT_OFF,
++ VIN_OUTPUT_RESERVED,
++ VIN_OUTPUT_SINGLE,
++ VIN_OUTPUT_CONTINUOUS,
++ VIN_OUTPUT_IDLE,
++ VIN_OUTPUT_STOPPING
++};
++
++struct vin_output {
++ int active_buf;
++ struct stfcamss_buffer *buf[2];
++ struct stfcamss_buffer *last_buffer;
++ struct list_head pending_bufs;
++ struct list_head ready_bufs;
++ enum vin_output_state state;
++ unsigned int sequence;
++ unsigned int frame_skip;
++};
++
++/* The vin output lines include all isp controller lines,
++ * and one vin_wr output line.
++ */
++enum vin_line_id {
++ VIN_LINE_NONE = -1,
++ VIN_LINE_WR = 0,
++ VIN_LINE_ISP = 1,
++ VIN_LINE_ISP_SS0 = 2,
++ VIN_LINE_ISP_SS1 = 3,
++ VIN_LINE_ISP_ITIW = 4,
++ VIN_LINE_ISP_ITIR = 5,
++ VIN_LINE_ISP_RAW = 6,
++ VIN_LINE_ISP_SCD_Y = 7,
++ VIN_LINE_MAX = 8,
++};
++
++enum subdev_type;
++
++struct vin_line {
++ enum subdev_type sdev_type; // must be frist
++ enum vin_line_id id;
++ struct v4l2_subdev subdev;
++ struct media_pad pads[STF_VIN_PADS_NUM];
++ struct v4l2_mbus_framefmt fmt[STF_VIN_PADS_NUM];
++ struct stfcamss_video video_out;
++ struct mutex stream_lock;
++ int stream_count;
++ struct mutex power_lock;
++ int power_count;
++ struct vin_output output;
++ spinlock_t output_lock;
++ const struct vin2_format *formats;
++ unsigned int nformats;
++#ifdef CONFIG_PM_SLEEP
++ int pm_stream_count;
++ int pm_power_count;
++#endif
++};
++
++struct stf_vin2_dev;
++
++struct vin_hw_ops {
++ int (*vin_clk_enable)(struct stf_vin2_dev *vin_dev);
++ int (*vin_clk_disable)(struct stf_vin2_dev *vin_dev);
++ int (*vin_config_set)(struct stf_vin2_dev *vin_dev);
++ int (*vin_wr_stream_set)(struct stf_vin2_dev *vin_dev, int on);
++ void (*vin_wr_irq_enable)(struct stf_vin2_dev *vin_dev, int enable);
++ void (*vin_power_on)(struct stf_vin2_dev *vin_dev, int on);
++ void (*wr_rd_set_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t wr_addr, dma_addr_t rd_addr);
++ void (*vin_wr_set_ping_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t addr);
++ void (*vin_wr_set_pong_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t addr);
++ void (*vin_wr_get_ping_pong_status)(struct stf_vin2_dev *vin_dev);
++ void (*vin_isp_set_yuv_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr);
++ void (*vin_isp_set_raw_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t raw_addr);
++ void (*vin_isp_set_ss0_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr);
++ void (*vin_isp_set_ss1_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr);
++ void (*vin_isp_set_itiw_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr);
++ void (*vin_isp_set_itir_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr);
++ void (*vin_isp_set_scd_addr)(struct stf_vin2_dev *vin_dev,
++ dma_addr_t yhist_addr,
++ dma_addr_t scd_addr, int scd_type);
++ int (*vin_isp_get_scd_type)(struct stf_vin2_dev *vin_dev);
++ irqreturn_t (*vin_wr_irq_handler)(int irq, void *priv);
++ irqreturn_t (*vin_isp_irq_handler)(int irq, void *priv);
++ irqreturn_t (*vin_isp_csi_irq_handler)(int irq, void *priv);
++ irqreturn_t (*vin_isp_scd_irq_handler)(int irq, void *priv);
++ irqreturn_t (*vin_isp_irq_csiline_handler)(int irq, void *priv);
++ void (*isr_buffer_done)(struct vin_line *line,
++ struct vin_params *params);
++ void (*isr_change_buffer)(struct vin_line *line);
++};
++
++#define ISP_DUMMY_BUFFER_NUMS STF_ISP_PAD_MAX
++#define VIN_DUMMY_BUFFER_NUMS 1
++
++enum {
++ STF_DUMMY_VIN,
++ STF_DUMMY_ISP,
++ STF_DUMMY_MODULE_NUMS,
++};
++
++struct vin_dummy_buffer {
++ dma_addr_t paddr[3];
++ void *vaddr;
++ u32 buffer_size;
++ u32 width;
++ u32 height;
++ u32 mcode;
++};
++
++struct dummy_buffer {
++ struct vin_dummy_buffer *buffer;
++ u32 nums;
++ struct mutex stream_lock;
++ int stream_count;
++ atomic_t frame_skip;
++};
++
++struct stf_vin2_dev {
++ struct stfcamss *stfcamss;
++ struct vin_line line[VIN_LINE_MAX];
++ struct dummy_buffer dummy_buffer[STF_DUMMY_MODULE_NUMS];
++ struct vin_hw_ops *hw_ops;
++ atomic_t ref_count;
++ struct mutex power_lock;
++ int power_count;
++};
++
++extern void sifive_l2_flush64_range(unsigned long start, unsigned long len);
++extern int stf_vin_subdev_init(struct stfcamss *stfcamss);
++extern int stf_vin_register(struct stf_vin2_dev *vin_dev,
++ struct v4l2_device *v4l2_dev);
++extern int stf_vin_unregister(struct stf_vin2_dev *vin_dev);
++
++extern struct vin_hw_ops vin_ops;
++extern void dump_vin_reg(void *__iomem regbase);
++extern enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line,
++ enum isp_pad_id def);
++
++#endif /* STF_VIN_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
+@@ -0,0 +1,433 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/of_graph.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static void vin_intr_clear(void __iomem *sysctrl_base)
++{
++ reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++ 0x1);
++ reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++ 0x0);
++}
++
++static irqreturn_t stf_vin_wr_irq_handler(int irq, void *priv)
++{
++ static struct vin_params params;
++ struct stf_vin2_dev *vin_dev = priv;
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ struct dummy_buffer *dummy_buffer =
++ &vin_dev->dummy_buffer[STF_DUMMY_VIN];
++
++ if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
++ vin_dev->hw_ops->isr_change_buffer(&vin_dev->line[VIN_LINE_WR]);
++ vin_dev->hw_ops->isr_buffer_done(&vin_dev->line[VIN_LINE_WR], ¶ms);
++ }
++
++ vin_intr_clear(vin->sysctrl_base);
++
++ return IRQ_HANDLED;
++}
++
++static void __iomem *stf_vin_get_ispbase(struct stf_vin_dev *vin)
++{
++ void __iomem *base = vin->isp_base;
++
++ return base;
++}
++
++static irqreturn_t stf_vin_isp_irq_handler(int irq, void *priv)
++{
++ static struct vin_params params;
++ struct stf_vin2_dev *vin_dev = priv;
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ u32 int_status, value;
++
++ ispbase = stf_vin_get_ispbase(vin);
++
++ int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++ if (int_status & BIT(24)) {
++ if ((int_status & BIT(11)))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_SS0], ¶ms);
++
++ if ((int_status & BIT(12)))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_SS1], ¶ms);
++
++ if ((int_status & BIT(20)))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP], ¶ms);
++
++ value = reg_read(ispbase, ISP_REG_ITIDPSR);
++ if ((value & BIT(17)))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_ITIW], ¶ms);
++ if ((value & BIT(16)))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_ITIR], ¶ms);
++
++#ifndef ISP_USE_CSI_AND_SC_DONE_INTERRUPT
++ if (int_status & BIT(25))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_RAW], ¶ms);
++
++ if (int_status & BIT(26))
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_SCD_Y], ¶ms);
++
++ /* clear interrupt */
++ reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL)
++ | EN_INT_ISP_DONE | EN_INT_CSI_DONE | EN_INT_SC_DONE);
++#else
++ /* clear interrupt */
++ reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++ (int_status & ~EN_INT_ALL) | EN_INT_ISP_DONE);
++#endif
++ } else
++ st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_csi_irq_handler(int irq, void *priv)
++{
++ static struct vin_params params;
++ struct stf_vin2_dev *vin_dev = priv;
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ u32 int_status;
++
++ ispbase = stf_vin_get_ispbase(vin);
++
++ int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++ if (int_status & BIT(25)) {
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_RAW], ¶ms);
++
++ /* clear interrupt */
++ reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++ (int_status & ~EN_INT_ALL) | EN_INT_CSI_DONE);
++ } else
++ st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_scd_irq_handler(int irq, void *priv)
++{
++ static struct vin_params params;
++ struct stf_vin2_dev *vin_dev = priv;
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase;
++ u32 int_status;
++
++ ispbase = stf_vin_get_ispbase(vin);
++
++ int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++ if (int_status & BIT(26)) {
++ vin_dev->hw_ops->isr_buffer_done(
++ &vin_dev->line[VIN_LINE_ISP_SCD_Y], ¶ms);
++
++ /* clear interrupt */
++ reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL) | EN_INT_SC_DONE);
++ } else
++ st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv)
++{
++ struct stf_vin2_dev *vin_dev = priv;
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ struct stf_isp_dev *isp_dev;
++ void __iomem *ispbase;
++ u32 int_status, value;
++
++ ispbase = stf_vin_get_ispbase(vin);
++
++ isp_dev = vin_dev->stfcamss->isp_dev;
++
++ int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++ if (int_status & BIT(27)) {
++ struct dummy_buffer *dummy_buffer =
++ &vin_dev->dummy_buffer[STF_DUMMY_ISP];
++
++ if (!atomic_read(&isp_dev->shadow_count)) {
++ if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
++ if ((int_status & BIT(11)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_SS0]);
++ if ((int_status & BIT(12)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_SS1]);
++ if ((int_status & BIT(20)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP]);
++
++ value = reg_read(ispbase, ISP_REG_ITIDPSR);
++ if ((value & BIT(17)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_ITIW]);
++ if ((value & BIT(16)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_ITIR]);
++
++ value = reg_read(ispbase, ISP_REG_CSI_MODULE_CFG);
++ if ((value & BIT(19)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_RAW]);
++ if ((value & BIT(17)))
++ vin_dev->hw_ops->isr_change_buffer(
++ &vin_dev->line[VIN_LINE_ISP_SCD_Y]);
++ }
++
++ // shadow update
++ reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, 0x30000, 0x30000);
++ reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, BIT(1) | BIT(0), 0x3);
++ } else {
++ st_warn(ST_VIN, "isp shadow_lock locked. skip this frame\n");
++ }
++
++ /* clear interrupt */
++ reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++ (int_status & ~EN_INT_ALL) | EN_INT_LINE_INT);
++ } else
++ st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++ return IRQ_HANDLED;
++}
++
++static int stf_vin_clk_enable(struct stf_vin2_dev *vin_dev)
++{
++ struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_PCLK].clk);
++ clk_set_rate(stfcamss->sys_clk[STFCLK_APB_FUNC].clk, 49500000);
++ clk_set_rate(stfcamss->sys_clk[STFCLK_SYS_CLK].clk, 297000000);
++
++ reset_control_deassert(stfcamss->sys_rst[STFRST_PCLK].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
++
++ return 0;
++}
++
++
++static int stf_vin_clk_disable(struct stf_vin2_dev *vin_dev)
++{
++ struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++ reset_control_assert(stfcamss->sys_rst[STFRST_PCLK].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
++
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PCLK].clk);
++
++ return 0;
++}
++
++static int stf_vin_config_set(struct stf_vin2_dev *vin_dev)
++{
++ return 0;
++}
++
++static int stf_vin_wr_stream_set(struct stf_vin2_dev *vin_dev, int on)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++ //make the axiwr alway on
++ if (on)
++ reg_set(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20, U0_VIN_CNFG_AXIWR0_EN);
++
++ return 0;
++}
++
++static void stf_vin_wr_irq_enable(struct stf_vin2_dev *vin_dev,
++ int enable)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ unsigned int value = 0;
++
++ if (enable) {
++ value = ~(0x1 << 1);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_MASK,
++ value);
++ } else {
++ /* clear vin interrupt */
++ value = 0x1 << 1;
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++ 0x1);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++ 0x0);
++ reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++ U0_VIN_CNFG_AXIWR0_MASK,
++ value);
++ }
++}
++
++static void stf_vin_wr_rd_set_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t wr_addr, dma_addr_t rd_addr)
++{
++#ifdef UNUSED_CODE
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++ /* set the start address*/
++ reg_write(vin->sysctrl_base,
++ SYSCTRL_VIN_WR_START_ADDR, (long)wr_addr);
++ reg_write(vin->sysctrl_base,
++ SYSCTRL_VIN_RD_END_ADDR, (long)rd_addr);
++#endif
++}
++
++void stf_vin_wr_set_ping_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++ /* set the start address */
++ reg_write(vin->sysctrl_base, SYSCONSAIF_SYSCFG_24, (long)addr);
++}
++
++void stf_vin_wr_set_pong_addr(struct stf_vin2_dev *vin_dev, dma_addr_t addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++ /* set the start address */
++ reg_write(vin->sysctrl_base, SYSCONSAIF_SYSCFG_32, (long)addr);
++}
++
++void stf_vin_isp_set_yuv_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_Y_PLANE_START_ADDR, y_addr);
++ reg_write(ispbase, ISP_REG_UV_PLANE_START_ADDR, uv_addr);
++ // reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(0), 1);
++}
++
++void stf_vin_isp_set_raw_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t raw_addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_DUMP_CFG_0, raw_addr);
++}
++
++void stf_vin_isp_set_ss0_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_SS0AY, y_addr);
++ reg_write(ispbase, ISP_REG_SS0AUV, uv_addr);
++}
++
++void stf_vin_isp_set_ss1_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_SS1AY, y_addr);
++ reg_write(ispbase, ISP_REG_SS1AUV, uv_addr);
++}
++
++void stf_vin_isp_set_itiw_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_ITIDWYSAR, y_addr);
++ reg_write(ispbase, ISP_REG_ITIDWUSAR, uv_addr);
++}
++
++void stf_vin_isp_set_itir_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_write(ispbase, ISP_REG_ITIDRYSAR, y_addr);
++ reg_write(ispbase, ISP_REG_ITIDRUSAR, uv_addr);
++}
++
++int stf_vin_isp_get_scd_type(struct stf_vin2_dev *vin_dev)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ return (reg_read(ispbase, ISP_REG_SC_CFG_1) & (0x3 << 30)) >> 30;
++}
++
++void stf_vin_isp_set_scd_addr(struct stf_vin2_dev *vin_dev,
++ dma_addr_t yhist_addr, dma_addr_t scd_addr, int scd_type)
++{
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++ reg_set_bit(ispbase, ISP_REG_SC_CFG_1, 0x3 << 30, scd_type << 30);
++ reg_write(ispbase, ISP_REG_SCD_CFG_0, scd_addr);
++ reg_write(ispbase, ISP_REG_YHIST_CFG_4, yhist_addr);
++}
++
++void dump_vin_reg(void *__iomem regbase)
++{
++ st_debug(ST_VIN, "DUMP VIN register:\n");
++ print_reg(ST_VIN, regbase, 0x00);
++ print_reg(ST_VIN, regbase, 0x04);
++ print_reg(ST_VIN, regbase, 0x08);
++ print_reg(ST_VIN, regbase, 0x0c);
++ print_reg(ST_VIN, regbase, 0x10);
++ print_reg(ST_VIN, regbase, 0x14);
++ print_reg(ST_VIN, regbase, 0x18);
++ print_reg(ST_VIN, regbase, 0x1c);
++ print_reg(ST_VIN, regbase, 0x20);
++ print_reg(ST_VIN, regbase, 0x24);
++ print_reg(ST_VIN, regbase, 0x28);
++}
++
++struct vin_hw_ops vin_ops = {
++ .vin_clk_enable = stf_vin_clk_enable,
++ .vin_clk_disable = stf_vin_clk_disable,
++ .vin_config_set = stf_vin_config_set,
++ .vin_wr_stream_set = stf_vin_wr_stream_set,
++ .vin_wr_irq_enable = stf_vin_wr_irq_enable,
++ .wr_rd_set_addr = stf_vin_wr_rd_set_addr,
++ .vin_wr_set_ping_addr = stf_vin_wr_set_ping_addr,
++ .vin_wr_set_pong_addr = stf_vin_wr_set_pong_addr,
++ .vin_isp_set_yuv_addr = stf_vin_isp_set_yuv_addr,
++ .vin_isp_set_raw_addr = stf_vin_isp_set_raw_addr,
++ .vin_isp_set_ss0_addr = stf_vin_isp_set_ss0_addr,
++ .vin_isp_set_ss1_addr = stf_vin_isp_set_ss1_addr,
++ .vin_isp_set_itiw_addr = stf_vin_isp_set_itiw_addr,
++ .vin_isp_set_itir_addr = stf_vin_isp_set_itir_addr,
++ .vin_isp_set_scd_addr = stf_vin_isp_set_scd_addr,
++ .vin_isp_get_scd_type = stf_vin_isp_get_scd_type,
++ .vin_wr_irq_handler = stf_vin_wr_irq_handler,
++ .vin_isp_irq_handler = stf_vin_isp_irq_handler,
++ .vin_isp_csi_irq_handler = stf_vin_isp_csi_irq_handler,
++ .vin_isp_scd_irq_handler = stf_vin_isp_scd_irq_handler,
++ .vin_isp_irq_csiline_handler = stf_vin_isp_irq_csiline_handler,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stfcamss.c
+@@ -0,0 +1,1369 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/dmaengine.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/of_graph.h>
++#include <linux/of_address.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/uaccess.h>
++#include <linux/mfd/syscon.h>
++
++#include <linux/videodev2.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-mc.h>
++#include <media/v4l2-fwnode.h>
++#include <linux/debugfs.h>
++
++#include "stfcamss.h"
++
++#ifdef STF_DEBUG
++unsigned int stdbg_level = ST_DEBUG;
++unsigned int stdbg_mask = 0x7F;
++#else
++unsigned int stdbg_level = ST_ERR;
++unsigned int stdbg_mask = 0x7F;
++#endif
++EXPORT_SYMBOL_GPL(stdbg_level);
++EXPORT_SYMBOL_GPL(stdbg_mask);
++
++static const struct reg_name mem_reg_name[] = {
++ {"csi2rx"},
++ {"vclk"},
++ {"vrst"},
++ {"sctrl"},
++ {"isp"},
++ {"trst"},
++ {"pmu"},
++ {"syscrg"},
++};
++
++static struct clk_bulk_data stfcamss_clocks[] = {
++ { .id = "clk_apb_func" },
++ { .id = "clk_pclk" },
++ { .id = "clk_sys_clk" },
++ { .id = "clk_wrapper_clk_c" },
++ { .id = "clk_dvp_inv" },
++ { .id = "clk_axiwr" },
++ { .id = "clk_mipi_rx0_pxl" },
++ { .id = "clk_pixel_clk_if0" },
++ { .id = "clk_pixel_clk_if1" },
++ { .id = "clk_pixel_clk_if2" },
++ { .id = "clk_pixel_clk_if3" },
++ { .id = "clk_m31dphy_cfgclk_in" },
++ { .id = "clk_m31dphy_refclk_in" },
++ { .id = "clk_m31dphy_txclkesc_lan0" },
++ { .id = "clk_ispcore_2x" },
++ { .id = "clk_isp_axi" },
++};
++
++static struct reset_control_bulk_data stfcamss_resets[] = {
++ { .id = "rst_wrapper_p" },
++ { .id = "rst_wrapper_c" },
++ { .id = "rst_pclk" },
++ { .id = "rst_sys_clk" },
++ { .id = "rst_axird" },
++ { .id = "rst_axiwr" },
++ { .id = "rst_pixel_clk_if0" },
++ { .id = "rst_pixel_clk_if1" },
++ { .id = "rst_pixel_clk_if2" },
++ { .id = "rst_pixel_clk_if3" },
++ { .id = "rst_m31dphy_hw" },
++ { .id = "rst_m31dphy_b09_always_on" },
++ { .id = "rst_isp_top_n" },
++ { .id = "rst_isp_top_axi" },
++};
++
++int stfcamss_get_mem_res(struct platform_device *pdev, struct stf_vin_dev *vin)
++{
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++ char *name;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(mem_reg_name); i++) {
++ name = (char *)(&mem_reg_name[i]);
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
++
++ if (!res)
++ return -EINVAL;
++
++ if (!strcmp(name, "csi2rx")) {
++ vin->csi2rx_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(vin->csi2rx_base))
++ return PTR_ERR(vin->csi2rx_base);
++ } else if (!strcmp(name, "vclk")) {
++ vin->clkgen_base = ioremap(res->start, resource_size(res));
++ if (!vin->clkgen_base)
++ return -ENOMEM;
++ } else if (!strcmp(name, "vrst")) {
++ vin->rstgen_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(vin->rstgen_base))
++ return PTR_ERR(vin->rstgen_base);
++ } else if (!strcmp(name, "sctrl")) {
++ vin->sysctrl_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(vin->sysctrl_base))
++ return PTR_ERR(vin->sysctrl_base);
++ } else if (!strcmp(name, "isp")) {
++ vin->isp_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(vin->isp_base))
++ return PTR_ERR(vin->isp_base);
++ } else if (!strcmp(name, "trst")) {
++ vin->vin_top_rstgen_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(vin->vin_top_rstgen_base))
++ return PTR_ERR(vin->vin_top_rstgen_base);
++ } else if (!strcmp(name, "pmu")) {
++ vin->pmu_test = ioremap(res->start, resource_size(res));
++ if (!vin->pmu_test)
++ return -ENOMEM;
++ } else if (!strcmp(name, "syscrg")) {
++ vin->sys_crg = ioremap(res->start, resource_size(res));
++ if (!vin->sys_crg)
++ return -ENOMEM;
++ } else {
++ st_err(ST_CAMSS, "Could not match resource name\n");
++ }
++ }
++
++ return 0;
++}
++
++int vin_parse_dt(struct device *dev, struct stf_vin_dev *vin)
++{
++ int ret = 0;
++ struct device_node *np = dev->of_node;
++
++ if (!np)
++ return -EINVAL;
++
++ return ret;
++}
++
++struct media_entity *stfcamss_find_sensor(struct media_entity *entity)
++{
++ struct media_pad *pad;
++
++ while (1) {
++ if (!entity->pads)
++ return NULL;
++
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ return NULL;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ return NULL;
++
++ entity = pad->entity;
++
++ if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
++ return entity;
++ }
++}
++
++static int stfcamss_of_parse_endpoint_node(struct device *dev,
++ struct device_node *node,
++ struct stfcamss_async_subdev *csd)
++{
++ struct v4l2_fwnode_endpoint vep = { { 0 } };
++ struct v4l2_mbus_config_parallel *parallel_bus = &vep.bus.parallel;
++ struct v4l2_mbus_config_mipi_csi2 *csi2_bus = &vep.bus.mipi_csi2;
++ struct dvp_cfg *dvp = &csd->interface.dvp;
++ struct csi2phy_cfg *csiphy = &csd->interface.csiphy;
++
++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
++ st_debug(ST_CAMSS, "%s: vep.base.port = 0x%x, id = 0x%x\n",
++ __func__, vep.base.port, vep.base.id);
++
++ csd->port = vep.base.port;
++ switch (csd->port) {
++ case DVP_SENSOR_PORT_NUMBER:
++ st_debug(ST_CAMSS, "%s, flags = 0x%x\n", __func__,
++ parallel_bus->flags);
++ dvp->flags = parallel_bus->flags;
++ dvp->bus_width = parallel_bus->bus_width;
++ dvp->data_shift = parallel_bus->data_shift;
++ break;
++ case CSI2RX_SENSOR_PORT_NUMBER:
++ st_debug(ST_CAMSS, "%s, CSI2 flags = 0x%x\n",
++ __func__, parallel_bus->flags);
++ csiphy->flags = csi2_bus->flags;
++ memcpy(csiphy->data_lanes,
++ csi2_bus->data_lanes, csi2_bus->num_data_lanes);
++ csiphy->clock_lane = csi2_bus->clock_lane;
++ csiphy->num_data_lanes = csi2_bus->num_data_lanes;
++ memcpy(csiphy->lane_polarities,
++ csi2_bus->lane_polarities,
++ csi2_bus->num_data_lanes + 1);
++ break;
++ default:
++ break;
++ };
++
++ return 0;
++}
++
++static int stfcamss_of_parse_ports(struct stfcamss *stfcamss)
++{
++ struct device *dev = stfcamss->dev;
++ struct device_node *node = NULL;
++ struct device_node *remote = NULL;
++ int ret, num_subdevs = 0;
++
++ for_each_endpoint_of_node(dev->of_node, node) {
++ struct stfcamss_async_subdev *csd;
++
++ if (!of_device_is_available(node))
++ continue;
++
++ remote = of_graph_get_remote_port_parent(node);
++ if (!remote) {
++ st_err(ST_CAMSS, "Cannot get remote parent\n");
++ ret = -EINVAL;
++ goto err_cleanup;
++ }
++
++ csd = v4l2_async_nf_add_fwnode(&stfcamss->notifier,
++ of_fwnode_handle(remote),
++ struct stfcamss_async_subdev);
++ of_node_put(remote);
++ if (IS_ERR(csd)) {
++ ret = PTR_ERR(csd);
++ goto err_cleanup;
++ }
++
++ ret = stfcamss_of_parse_endpoint_node(dev, node, csd);
++ if (ret < 0)
++ goto err_cleanup;
++
++ num_subdevs++;
++ }
++
++ return num_subdevs;
++
++err_cleanup:
++ of_node_put(node);
++ return ret;
++}
++
++static int stfcamss_init_subdevices(struct stfcamss *stfcamss)
++{
++ int ret;
++
++ ret = stf_dvp_subdev_init(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to init stf_dvp sub-device: %d\n",
++ ret);
++ return ret;
++ }
++
++ ret = stf_csiphy_subdev_init(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to init stf_csiphy sub-device: %d\n",
++ ret);
++ return ret;
++ }
++
++ ret = stf_csi_subdev_init(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to init stf_csi sub-device: %d\n",
++ ret);
++ return ret;
++ }
++
++ ret = stf_isp_subdev_init(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to init stf_isp sub-device: %d\n",
++ ret);
++ return ret;
++ }
++
++ ret = stf_vin_subdev_init(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to init stf_vin sub-device: %d\n",
++ ret);
++ return ret;
++ }
++ return ret;
++}
++
++static int stfcamss_register_subdevices(struct stfcamss *stfcamss)
++{
++ int ret;
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++ struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++ struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++ struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++
++ ret = stf_dvp_register(dvp_dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register stf dvp%d entity: %d\n",
++ 0, ret);
++ goto err_reg_dvp;
++ }
++
++ ret = stf_csiphy_register(csiphy_dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register stf csiphy%d entity: %d\n",
++ 0, ret);
++ goto err_reg_csiphy;
++ }
++
++ ret = stf_csi_register(csi_dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register stf csi%d entity: %d\n",
++ 0, ret);
++ goto err_reg_csi;
++ }
++
++ ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register stf isp%d entity: %d\n",
++ 0, ret);
++ goto err_reg_isp;
++ }
++
++ ret = stf_vin_register(vin_dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register vin entity: %d\n",
++ ret);
++ goto err_reg_vin;
++ }
++
++ ret = media_create_pad_link(
++ &dvp_dev->subdev.entity,
++ STF_DVP_PAD_SRC,
++ &vin_dev->line[VIN_LINE_WR].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->vin entities: %d\n",
++ dvp_dev->subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &csi_dev->subdev.entity,
++ STF_CSI_PAD_SRC,
++ &vin_dev->line[VIN_LINE_WR].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->vin entities: %d\n",
++ csi_dev->subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &csiphy_dev->subdev.entity,
++ STF_CSIPHY_PAD_SRC,
++ &csi_dev->subdev.entity,
++ STF_CSI_PAD_SINK,
++ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ csiphy_dev->subdev.entity.name,
++ csi_dev->subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC,
++ &vin_dev->line[VIN_LINE_ISP].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_SS0,
++ &vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_SS0]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_SS1,
++ &vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_SS1]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++#ifndef STF_CAMSS_SKIP_ITI
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_ITIW,
++ &vin_dev->line[VIN_LINE_ISP_ITIW].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_ITIW]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_ITIR,
++ &vin_dev->line[VIN_LINE_ISP_ITIR].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_ITIR]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++#endif
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_RAW,
++ &vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_RAW]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SRC_SCD_Y,
++ &vin_dev->line[VIN_LINE_ISP_SCD_Y].subdev.entity,
++ STF_VIN_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ isp_dev->subdev.entity.name,
++ vin_dev->line[VIN_LINE_ISP_SCD_Y]
++ .subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &dvp_dev->subdev.entity,
++ STF_DVP_PAD_SRC,
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ dvp_dev->subdev.entity.name,
++ isp_dev->subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ ret = media_create_pad_link(
++ &csi_dev->subdev.entity,
++ STF_CSI_PAD_SRC,
++ &isp_dev->subdev.entity,
++ STF_ISP_PAD_SINK,
++ 0);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ csi_dev->subdev.entity.name,
++ isp_dev->subdev.entity.name,
++ ret);
++ goto err_link;
++ }
++
++ return ret;
++
++err_link:
++ stf_vin_unregister(stfcamss->vin_dev);
++err_reg_vin:
++ stf_isp_unregister(stfcamss->isp_dev);
++err_reg_isp:
++ stf_csi_unregister(stfcamss->csi_dev);
++err_reg_csi:
++ stf_csiphy_unregister(stfcamss->csiphy_dev);
++err_reg_csiphy:
++ stf_dvp_unregister(stfcamss->dvp_dev);
++err_reg_dvp:
++ return ret;
++}
++
++static void stfcamss_unregister_subdevices(struct stfcamss *stfcamss)
++{
++ stf_dvp_unregister(stfcamss->dvp_dev);
++ stf_csiphy_unregister(stfcamss->csiphy_dev);
++ stf_csi_unregister(stfcamss->csi_dev);
++ stf_isp_unregister(stfcamss->isp_dev);
++ stf_vin_unregister(stfcamss->vin_dev);
++}
++
++static int stfcamss_register_mediadevice_subdevnodes(
++ struct v4l2_async_notifier *async,
++ struct v4l2_subdev *sd)
++{
++ struct stfcamss *stfcamss =
++ container_of(async, struct stfcamss, notifier);
++ int ret;
++
++ if (sd->host_priv) {
++ struct media_entity *sensor = &sd->entity;
++ struct media_entity *input = sd->host_priv;
++ unsigned int i;
++
++ for (i = 0; i < sensor->num_pads; i++) {
++ if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
++ break;
++ }
++ if (i == sensor->num_pads) {
++ st_err(ST_CAMSS,
++ "No source pad in external entity\n");
++ return -EINVAL;
++ }
++
++ ret = media_create_pad_link(sensor, i,
++ input, STF_PAD_SINK,
++ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ sensor->name, input->name, ret);
++ return ret;
++ }
++ }
++
++ ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++ if (ret < 0)
++ return ret;
++
++ if (stfcamss->media_dev.devnode)
++ return ret;
++
++ st_debug(ST_CAMSS, "stfcamss register media device\n");
++ return media_device_register(&stfcamss->media_dev);
++}
++
++static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async,
++ struct v4l2_subdev *subdev,
++ struct v4l2_async_connection *asd)
++{
++ struct stfcamss *stfcamss =
++ container_of(async, struct stfcamss, notifier);
++ struct stfcamss_async_subdev *csd =
++ container_of(asd, struct stfcamss_async_subdev, asd);
++ enum port_num port = csd->port;
++ struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++ struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++
++ switch (port) {
++ case DVP_SENSOR_PORT_NUMBER:
++ dvp_dev->dvp = &csd->interface.dvp;
++ subdev->host_priv = &dvp_dev->subdev.entity;
++ break;
++ case CSI2RX_SENSOR_PORT_NUMBER:
++ csiphy_dev->csiphy = &csd->interface.csiphy;
++ subdev->host_priv = &csiphy_dev->subdev.entity;
++ break;
++ default:
++ break;
++ };
++
++ stfcamss_register_mediadevice_subdevnodes(async, subdev);
++
++ return 0;
++}
++
++#ifdef UNUSED_CODE
++static int stfcamss_subdev_notifier_complete(
++ struct v4l2_async_notifier *async)
++{
++ struct stfcamss *stfcamss =
++ container_of(async, struct stfcamss, notifier);
++ struct v4l2_device *v4l2_dev = &stfcamss->v4l2_dev;
++ struct v4l2_subdev *sd;
++ int ret;
++
++ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
++ if (sd->host_priv) {
++ struct media_entity *sensor = &sd->entity;
++ struct media_entity *input = sd->host_priv;
++ unsigned int i;
++
++ for (i = 0; i < sensor->num_pads; i++) {
++ if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
++ break;
++ }
++ if (i == sensor->num_pads) {
++ st_err(ST_CAMSS,
++ "No source pad in external entity\n");
++ return -EINVAL;
++ }
++
++ ret = media_create_pad_link(sensor, i,
++ input, STF_PAD_SINK,
++ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to link %s->%s entities: %d\n",
++ sensor->name, input->name, ret);
++ return ret;
++ }
++ }
++ }
++
++ ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++ if (ret < 0)
++ return ret;
++
++ return media_device_register(&stfcamss->media_dev);
++}
++#endif
++
++static const struct v4l2_async_notifier_operations
++stfcamss_subdev_notifier_ops = {
++ .bound = stfcamss_subdev_notifier_bound,
++};
++
++static const struct media_device_ops stfcamss_media_ops = {
++ .link_notify = v4l2_pipeline_link_notify,
++};
++
++#ifdef CONFIG_DEBUG_FS
++enum module_id {
++ VIN_MODULE = 0,
++ ISP_MODULE,
++ CSI_MODULE,
++ CSIPHY_MODULE,
++ DVP_MODULE,
++ CLK_MODULE,
++};
++
++static enum module_id id_num = ISP_MODULE;
++
++void dump_clk_reg(void __iomem *reg_base)
++{
++ int i;
++
++ st_info(ST_CAMSS, "DUMP Clk register:\n");
++ for (i = 0; i <= CLK_C_ISP_CTRL; i += 4)
++ print_reg(ST_CAMSS, reg_base, i);
++}
++
++static ssize_t vin_debug_read(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct device *dev = file->private_data;
++ void __iomem *reg_base;
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++ struct stf_vin_dev *vin = stfcamss->vin;
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++ struct stf_csi_dev *csi0_dev = stfcamss->csi_dev;
++
++ switch (id_num) {
++ case VIN_MODULE:
++ case CSIPHY_MODULE:
++ case DVP_MODULE:
++ mutex_lock(&vin_dev->power_lock);
++ if (vin_dev->power_count > 0) {
++ reg_base = vin->sysctrl_base;
++ dump_vin_reg(reg_base);
++ }
++ mutex_unlock(&vin_dev->power_lock);
++ break;
++ case ISP_MODULE:
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count > 0) {
++ reg_base = vin->isp_base;
++ dump_isp_reg(reg_base);
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++ break;
++ case CSI_MODULE:
++ mutex_lock(&csi0_dev->stream_lock);
++ if (csi0_dev->stream_count > 0) {
++ reg_base = vin->csi2rx_base;
++ dump_csi_reg(reg_base);
++ }
++ mutex_unlock(&csi0_dev->stream_lock);
++ break;
++ case CLK_MODULE:
++ mutex_lock(&vin_dev->power_lock);
++ if (vin_dev->power_count > 0) {
++ reg_base = vin->clkgen_base;
++ dump_clk_reg(reg_base);
++ }
++ mutex_unlock(&vin_dev->power_lock);
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static void set_reg_val(struct stfcamss *stfcamss, int id, u32 offset, u32 val)
++{
++ struct stf_vin_dev *vin = stfcamss->vin;
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++ struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++ void __iomem *reg_base;
++
++ switch (id) {
++ case VIN_MODULE:
++ case CSIPHY_MODULE:
++ case DVP_MODULE:
++ mutex_lock(&vin_dev->power_lock);
++ if (vin_dev->power_count > 0) {
++ reg_base = vin->sysctrl_base;
++ print_reg(ST_VIN, reg_base, offset);
++ reg_write(reg_base, offset, val);
++ print_reg(ST_VIN, reg_base, offset);
++ }
++ mutex_unlock(&vin_dev->power_lock);
++ break;
++ case ISP_MODULE:
++ mutex_lock(&isp_dev->stream_lock);
++ if (isp_dev->stream_count > 0) {
++ reg_base = vin->isp_base;
++ print_reg(ST_ISP, reg_base, offset);
++ reg_write(reg_base, offset, val);
++ print_reg(ST_ISP, reg_base, offset);
++ }
++ mutex_unlock(&isp_dev->stream_lock);
++ break;
++ case CSI_MODULE:
++ mutex_lock(&csi_dev->stream_lock);
++ if (csi_dev->stream_count > 0) {
++ reg_base = vin->csi2rx_base;
++ print_reg(ST_CSI, reg_base, offset);
++ reg_write(reg_base, offset, val);
++ print_reg(ST_CSI, reg_base, offset);
++ }
++ mutex_unlock(&csi_dev->stream_lock);
++ break;
++ case CLK_MODULE:
++ mutex_lock(&vin_dev->power_lock);
++ if (vin_dev->power_count > 0) {
++ reg_base = vin->clkgen_base;
++ print_reg(ST_CAMSS, reg_base, offset);
++ reg_write(reg_base, offset, val);
++ print_reg(ST_CAMSS, reg_base, offset);
++ }
++ mutex_unlock(&vin_dev->power_lock);
++ break;
++ default:
++ break;
++
++ }
++}
++
++static u32 atoi(const char *s)
++{
++ u32 ret = 0, d = 0;
++ char ch;
++ int hex = 0;
++
++ if ((*s == '0') && (*(s+1) == 'x')) {
++ hex = 1;
++ s += 2;
++ }
++
++ while (1) {
++ if (!hex) {
++ d = (*s++) - '0';
++ if (d > 9)
++ break;
++ ret *= 10;
++ ret += d;
++ } else {
++ ch = tolower(*s++);
++ if (isdigit(ch))
++ d = ch - '0';
++ else if (islower(ch))
++ d = ch - 'a' + 10;
++ else
++ break;
++ if (d > 15)
++ break;
++ ret *= 16;
++ ret += d;
++ }
++ }
++
++ return ret;
++}
++
++static ssize_t vin_debug_write(struct file *file, const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct device *dev = file->private_data;
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++ char *buf;
++ char *line;
++ char *p;
++ static const char *delims = " \t\r";
++ char *token;
++ u32 offset, val;
++
++ buf = memdup_user_nul(user_buf, min_t(size_t, PAGE_SIZE, count));
++ if (IS_ERR(buf))
++ return PTR_ERR(buf);
++ p = buf;
++ st_debug(ST_CAMSS, "dup buf: %s, len: %lu, count: %lu\n", p, strlen(p), count);
++ while (p && *p) {
++ p = skip_spaces(p);
++ line = strsep(&p, "\n");
++ if (!*line || *line == '#')
++ break;
++ token = strsep(&line, delims);
++ if (!token)
++ goto out;
++ id_num = atoi(token);
++ token = strsep(&line, delims);
++ if (!token)
++ goto out;
++ offset = atoi(token);
++ token = strsep(&line, delims);
++ if (!token)
++ goto out;
++ val = atoi(token);
++ }
++ set_reg_val(stfcamss, id_num, offset, val);
++out:
++ kfree(buf);
++ st_info(ST_CAMSS, "id_num = %d, offset = 0x%x, 0x%x\n", id_num, offset, val);
++ return count;
++}
++
++static const struct file_operations vin_debug_fops = {
++ .open = simple_open,
++ .read = vin_debug_read,
++ .write = vin_debug_write,
++};
++#endif /* CONFIG_DEBUG_FS */
++
++
++static int stfcamss_probe(struct platform_device *pdev)
++{
++ struct stfcamss *stfcamss;
++ struct stf_vin_dev *vin;
++ struct device *dev = &pdev->dev;
++ struct of_phandle_args args;
++ int ret = 0, num_subdevs;
++
++ dev_info(dev, "stfcamss probe enter!\n");
++
++ stfcamss = devm_kzalloc(dev, sizeof(struct stfcamss), GFP_KERNEL);
++ if (!stfcamss)
++ return -ENOMEM;
++
++ stfcamss->dvp_dev = devm_kzalloc(dev,
++ sizeof(*stfcamss->dvp_dev), GFP_KERNEL);
++ if (!stfcamss->dvp_dev) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ stfcamss->csiphy_dev = devm_kzalloc(dev,
++ sizeof(*stfcamss->csiphy_dev),
++ GFP_KERNEL);
++ if (!stfcamss->csiphy_dev) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ stfcamss->csi_dev = devm_kzalloc(dev,
++ sizeof(*stfcamss->csi_dev),
++ GFP_KERNEL);
++ if (!stfcamss->csi_dev) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ stfcamss->isp_dev = devm_kzalloc(dev,
++ sizeof(*stfcamss->isp_dev),
++ GFP_KERNEL);
++ if (!stfcamss->isp_dev) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ stfcamss->vin_dev = devm_kzalloc(dev,
++ sizeof(*stfcamss->vin_dev),
++ GFP_KERNEL);
++ if (!stfcamss->vin_dev) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ stfcamss->vin = devm_kzalloc(dev,
++ sizeof(struct stf_vin_dev),
++ GFP_KERNEL);
++ if (!stfcamss->vin) {
++ ret = -ENOMEM;
++ goto err_cam;
++ }
++
++ vin = stfcamss->vin;
++
++ vin->irq = platform_get_irq(pdev, 0);
++ if (vin->irq <= 0) {
++ st_err(ST_CAMSS, "Could not get irq\n");
++ goto err_cam;
++ }
++
++ vin->isp_irq = platform_get_irq(pdev, 1);
++ if (vin->isp_irq <= 0) {
++ st_err(ST_CAMSS, "Could not get isp irq\n");
++ goto err_cam;
++ }
++
++ vin->isp_csi_irq = platform_get_irq(pdev, 2);
++ if (vin->isp_csi_irq <= 0) {
++ st_err(ST_CAMSS, "Could not get isp csi irq\n");
++ goto err_cam;
++ }
++
++ vin->isp_scd_irq = platform_get_irq(pdev, 3);
++ if (vin->isp_scd_irq <= 0) {
++ st_err(ST_CAMSS, "Could not get isp scd irq\n");
++ goto err_cam;
++ }
++
++ vin->isp_irq_csiline = platform_get_irq(pdev, 4);
++ if (vin->isp_irq_csiline <= 0) {
++ st_err(ST_CAMSS, "Could not get isp irq csiline\n");
++ goto err_cam;
++ }
++
++ pm_runtime_enable(dev);
++
++ stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks);
++ stfcamss->sys_clk = stfcamss_clocks;
++
++ ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk);
++ if (ret) {
++ st_err(ST_CAMSS, "Failed to get clk controls\n");
++ return ret;
++ }
++
++ stfcamss->nrsts = ARRAY_SIZE(stfcamss_resets);
++ stfcamss->sys_rst = stfcamss_resets;
++
++ ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
++ stfcamss->sys_rst);
++ if (ret) {
++ st_err(ST_CAMSS, "Failed to get reset controls\n");
++ return ret;
++ }
++
++ ret = of_parse_phandle_with_fixed_args(dev->of_node,
++ "starfive,aon-syscon", 1, 0, &args);
++ if (ret < 0) {
++ st_err(ST_CAMSS, "Failed to parse starfive,aon-syscon\n");
++ return -EINVAL;
++ }
++
++ stfcamss->stf_aon_syscon = syscon_node_to_regmap(args.np);
++ of_node_put(args.np);
++ if (IS_ERR(stfcamss->stf_aon_syscon))
++ return PTR_ERR(stfcamss->stf_aon_syscon);
++
++ stfcamss->aon_gp_reg = args.args[0];
++
++ ret = stfcamss_get_mem_res(pdev, vin);
++ if (ret) {
++ st_err(ST_CAMSS, "Could not map registers\n");
++ goto err_cam;
++ }
++
++ ret = vin_parse_dt(dev, vin);
++ if (ret)
++ goto err_cam;
++
++ vin->dev = dev;
++ stfcamss->dev = dev;
++ platform_set_drvdata(pdev, stfcamss);
++
++ v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev);
++
++ num_subdevs = stfcamss_of_parse_ports(stfcamss);
++ if (num_subdevs < 0) {
++ ret = num_subdevs;
++ goto err_cam_noti;
++ }
++
++ ret = stfcamss_init_subdevices(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS, "Failed to init subdevice: %d\n", ret);
++ goto err_cam_noti;
++ }
++
++ stfcamss->media_dev.dev = stfcamss->dev;
++ strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem",
++ sizeof(stfcamss->media_dev.model));
++ strscpy(stfcamss->media_dev.serial, "0123456789ABCDEF",
++ sizeof(stfcamss->media_dev.serial));
++ snprintf(stfcamss->media_dev.bus_info, sizeof(stfcamss->media_dev.bus_info),
++ "%s:%s", dev_bus_name(dev), pdev->name);
++ stfcamss->media_dev.hw_revision = 0x01;
++ stfcamss->media_dev.ops = &stfcamss_media_ops;
++ media_device_init(&stfcamss->media_dev);
++
++ stfcamss->v4l2_dev.mdev = &stfcamss->media_dev;
++
++ ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS, "Failed to register V4L2 device: %d\n", ret);
++ goto err_cam_noti_med;
++ }
++
++ ret = stfcamss_register_subdevices(stfcamss);
++ if (ret < 0) {
++ st_err(ST_CAMSS, "Failed to register subdevice: %d\n", ret);
++ goto err_cam_noti_med_vreg;
++ }
++
++ if (num_subdevs) {
++ stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops;
++ ret = v4l2_async_nf_register(&stfcamss->notifier);
++ if (ret) {
++ st_err(ST_CAMSS,
++ "Failed to register async subdev nodes: %d\n",
++ ret);
++ goto err_cam_noti_med_vreg_sub;
++ }
++ } else {
++ ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS,
++ "Failed to register subdev nodes: %d\n",
++ ret);
++ goto err_cam_noti_med_vreg_sub;
++ }
++
++ ret = media_device_register(&stfcamss->media_dev);
++ if (ret < 0) {
++ st_err(ST_CAMSS, "Failed to register media device: %d\n",
++ ret);
++ goto err_cam_noti_med_vreg_sub_medreg;
++ }
++ }
++
++#ifdef CONFIG_DEBUG_FS
++ stfcamss->debugfs_entry = debugfs_create_dir("stfcamss", NULL);
++ stfcamss->vin_debugfs = debugfs_create_file("stf_vin",
++ 0644, stfcamss->debugfs_entry,
++ (void *)dev, &vin_debug_fops);
++ debugfs_create_u32("dbg_level",
++ 0644, stfcamss->debugfs_entry,
++ &stdbg_level);
++ debugfs_create_u32("dbg_mask",
++ 0644, stfcamss->debugfs_entry,
++ &stdbg_mask);
++#endif
++ dev_info(dev, "stfcamss probe success!\n");
++
++ return 0;
++
++#ifdef CONFIG_DEBUG_FS
++ debugfs_remove(stfcamss->vin_debugfs);
++ debugfs_remove_recursive(stfcamss->debugfs_entry);
++ stfcamss->debugfs_entry = NULL;
++#endif
++
++err_cam_noti_med_vreg_sub_medreg:
++err_cam_noti_med_vreg_sub:
++ stfcamss_unregister_subdevices(stfcamss);
++err_cam_noti_med_vreg:
++ v4l2_device_unregister(&stfcamss->v4l2_dev);
++err_cam_noti_med:
++ media_device_cleanup(&stfcamss->media_dev);
++err_cam_noti:
++ v4l2_async_nf_cleanup(&stfcamss->notifier);
++err_cam:
++ // kfree(stfcamss);
++ return ret;
++}
++
++static int stfcamss_remove(struct platform_device *pdev)
++{
++ struct stfcamss *stfcamss = platform_get_drvdata(pdev);
++
++ dev_info(&pdev->dev, "remove done\n");
++
++#ifdef CONFIG_DEBUG_FS
++ debugfs_remove(stfcamss->vin_debugfs);
++ debugfs_remove_recursive(stfcamss->debugfs_entry);
++ stfcamss->debugfs_entry = NULL;
++#endif
++
++ stfcamss_unregister_subdevices(stfcamss);
++ v4l2_device_unregister(&stfcamss->v4l2_dev);
++ media_device_cleanup(&stfcamss->media_dev);
++ pm_runtime_disable(&pdev->dev);
++
++ kfree(stfcamss);
++
++ return 0;
++}
++
++static const struct of_device_id stfcamss_of_match[] = {
++ { .compatible = "starfive,jh7110-vin" },
++ { /* end node */ },
++};
++
++MODULE_DEVICE_TABLE(of, stfcamss_of_match);
++
++#ifdef CONFIG_PM_SLEEP
++static int stfcamss_suspend(struct device *dev)
++{
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ struct media_entity *entity;
++ struct media_pad *pad;
++ struct v4l2_subdev *subdev;
++ struct stfcamss_video *video;
++ struct video_device *vdev;
++ int i = 0;
++ int pm_power_count;
++ int pm_stream_count;
++
++ for (i = 0; i < VIN_LINE_MAX; i++) {
++ video = &vin_dev->line[i].video_out;
++ vdev = &vin_dev->line[i].video_out.vdev;
++ vin_dev->line[i].pm_power_count = vin_dev->line[i].power_count;
++ vin_dev->line[i].pm_stream_count = vin_dev->line[i].stream_count;
++ pm_power_count = vin_dev->line[i].pm_power_count;
++ pm_stream_count = vin_dev->line[i].pm_stream_count;
++
++ if (pm_stream_count) {
++ while (pm_stream_count--) {
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ v4l2_subdev_call(subdev, video, s_stream, 0);
++ }
++ }
++ video_device_pipeline_stop(vdev);
++ video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
++ }
++
++ if (!pm_power_count)
++ continue;
++
++ v4l2_pipeline_pm_put(&vdev->entity);
++ }
++
++ return pm_runtime_force_suspend(dev);
++}
++
++static int stfcamss_resume(struct device *dev)
++{
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++ struct media_entity *entity;
++ struct media_pad *pad;
++ struct v4l2_subdev *subdev;
++ struct stfcamss_video *video;
++ struct video_device *vdev;
++ int i = 0;
++ int pm_power_count;
++ int pm_stream_count;
++ int ret = 0;
++
++ pm_runtime_force_resume(dev);
++
++ for (i = 0; i < VIN_LINE_MAX; i++) {
++ video = &vin_dev->line[i].video_out;
++ vdev = &vin_dev->line[i].video_out.vdev;
++ pm_power_count = vin_dev->line[i].pm_power_count;
++ pm_stream_count = vin_dev->line[i].pm_stream_count;
++
++ if (!pm_power_count)
++ continue;
++
++ ret = v4l2_pipeline_pm_get(&vdev->entity);
++ if (ret < 0)
++ goto err;
++
++ if (pm_stream_count) {
++ ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
++ if (ret < 0)
++ goto err_pm_put;
++
++ while (pm_stream_count--) {
++ entity = &vdev->entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ break;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ break;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++
++ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
++ if (ret < 0 && ret != -ENOIOCTLCMD)
++ goto err_pipeline_stop;
++ }
++ }
++ }
++ }
++
++ return 0;
++
++err_pipeline_stop:
++ video_device_pipeline_stop(vdev);
++err_pm_put:
++ v4l2_pipeline_pm_put(&vdev->entity);
++err:
++ return ret;
++}
++#endif /* CONFIG_PM_SLEEP */
++
++#ifdef CONFIG_PM
++static int stfcamss_runtime_suspend(struct device *dev)
++{
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++
++ reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
++ reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
++ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
++
++ return 0;
++}
++
++static int stfcamss_runtime_resume(struct device *dev)
++{
++ struct stfcamss *stfcamss = dev_get_drvdata(dev);
++
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
++ clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
++ reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
++
++ return 0;
++}
++#endif /* CONFIG_PM */
++
++static const struct dev_pm_ops stfcamss_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume)
++ SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, stfcamss_runtime_resume, NULL)
++};
++
++static struct platform_driver stfcamss_driver = {
++ .probe = stfcamss_probe,
++ .remove = stfcamss_remove,
++ .driver = {
++ .name = DRV_NAME,
++ .pm = &stfcamss_pm_ops,
++ .of_match_table = of_match_ptr(stfcamss_of_match),
++ },
++};
++
++static int __init stfcamss_init(void)
++{
++ return platform_driver_register(&stfcamss_driver);
++}
++
++static void __exit stfcamss_cleanup(void)
++{
++ platform_driver_unregister(&stfcamss_driver);
++}
++
++module_init(stfcamss_init);
++//fs_initcall(stfcamss_init);
++module_exit(stfcamss_cleanup);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stfcamss.h
+@@ -0,0 +1,117 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STFCAMSS_H
++#define STFCAMSS_H
++
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++
++enum sensor_type {
++ SENSOR_VIN,
++ /* need replace sensor */
++ SENSOR_ISP,
++};
++
++enum subdev_type {
++ VIN_DEV_TYPE,
++ ISP_DEV_TYPE,
++};
++
++#include "stf_common.h"
++#include "stf_dvp.h"
++#include "stf_csi.h"
++#include "stf_csiphy.h"
++#include "stf_isp.h"
++#include "stf_vin.h"
++
++#define STF_PAD_SINK 0
++#define STF_PAD_SRC 1
++#define STF_PADS_NUM 2
++
++#define STF_CAMSS_SKIP_ITI
++
++enum port_num {
++ DVP_SENSOR_PORT_NUMBER = 0,
++ CSI2RX_SENSOR_PORT_NUMBER
++};
++
++enum stf_clk_num {
++ STFCLK_APB_FUNC = 0,
++ STFCLK_PCLK,
++ STFCLK_SYS_CLK,
++ STFCLK_WRAPPER_CLK_C,
++ STFCLK_DVP_INV,
++ STFCLK_AXIWR,
++ STFCLK_MIPI_RX0_PXL,
++ STFCLK_PIXEL_CLK_IF0,
++ STFCLK_PIXEL_CLK_IF1,
++ STFCLK_PIXEL_CLK_IF2,
++ STFCLK_PIXEL_CLK_IF3,
++ STFCLK_M31DPHY_CFGCLK_IN,
++ STFCLK_M31DPHY_REFCLK_IN,
++ STFCLK_M31DPHY_TXCLKESC_LAN0,
++ STFCLK_ISPCORE_2X,
++ STFCLK_ISP_AXI,
++ STFCLK_NUM
++};
++
++enum stf_rst_num {
++ STFRST_WRAPPER_P = 0,
++ STFRST_WRAPPER_C,
++ STFRST_PCLK,
++ STFRST_SYS_CLK,
++ STFRST_AXIRD,
++ STFRST_AXIWR,
++ STFRST_PIXEL_CLK_IF0,
++ STFRST_PIXEL_CLK_IF1,
++ STFRST_PIXEL_CLK_IF2,
++ STFRST_PIXEL_CLK_IF3,
++ STFRST_M31DPHY_HW,
++ STFRST_M31DPHY_B09_ALWAYS_ON,
++ STFRST_ISP_TOP_N,
++ STFRST_ISP_TOP_AXI,
++ STFRST_NUM
++};
++
++struct stfcamss {
++ struct stf_vin_dev *vin; // stfcamss phy res
++ struct v4l2_device v4l2_dev;
++ struct media_device media_dev;
++ struct media_pipeline pipe;
++ struct device *dev;
++ struct stf_vin2_dev *vin_dev; // subdev
++ struct stf_dvp_dev *dvp_dev; // subdev
++ struct stf_csi_dev *csi_dev; // subdev
++ struct stf_csiphy_dev *csiphy_dev; // subdev
++ struct stf_isp_dev *isp_dev; // subdev
++ struct v4l2_async_notifier notifier;
++ struct clk_bulk_data *sys_clk;
++ int nclks;
++ struct reset_control_bulk_data *sys_rst;
++ int nrsts;
++ struct regmap *stf_aon_syscon;
++ uint32_t aon_gp_reg;
++#ifdef CONFIG_DEBUG_FS
++ struct dentry *debugfs_entry;
++ struct dentry *vin_debugfs;
++#endif
++};
++
++struct stfcamss_async_subdev {
++ struct v4l2_async_connection asd; // must be first
++ enum port_num port;
++ struct {
++ struct dvp_cfg dvp;
++ struct csi2phy_cfg csiphy;
++ } interface;
++};
++
++extern struct media_entity *stfcamss_find_sensor(struct media_entity *entity);
++
++#endif /* STFCAMSS_H */
+--- /dev/null
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -0,0 +1,253 @@
++/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
++/*
++ * jh7110-isp.h
++ *
++ * JH7110 ISP driver - user space header file.
++ *
++ * Copyright © 2023 Starfive Technology Co., Ltd.
++ *
++ * Author: Su Zejian (zejian.su@starfivetech.com)
++ *
++ */
++
++#ifndef __JH7110_ISP_H_
++#define __JH7110_ISP_H_
++
++#include <linux/v4l2-controls.h>
++
++#define V4L2_CID_USER_JH7110_ISP_WB_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
++#define V4L2_CID_USER_JH7110_ISP_CAR_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0002)
++#define V4L2_CID_USER_JH7110_ISP_CCM_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0003)
++#define V4L2_CID_USER_JH7110_ISP_CFA_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0004)
++#define V4L2_CID_USER_JH7110_ISP_CTC_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0005)
++#define V4L2_CID_USER_JH7110_ISP_DBC_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0006)
++#define V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0007)
++#define V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0008)
++#define V4L2_CID_USER_JH7110_ISP_LCCF_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0009)
++#define V4L2_CID_USER_JH7110_ISP_OBC_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000a)
++#define V4L2_CID_USER_JH7110_ISP_OECF_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000b)
++#define V4L2_CID_USER_JH7110_ISP_R2Y_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000c)
++#define V4L2_CID_USER_JH7110_ISP_SAT_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000d)
++#define V4L2_CID_USER_JH7110_ISP_SHRP_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
++#define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
++
++struct jh7110_isp_wb_gain {
++ __u16 gain_r;
++ __u16 gain_g;
++ __u16 gain_b;
++};
++
++struct jh7110_isp_wb_setting {
++ __u32 enabled;
++ struct jh7110_isp_wb_gain gains;
++};
++
++struct jh7110_isp_car_setting {
++ __u32 enabled;
++};
++
++struct jh7110_isp_ccm_smlow {
++ __s32 ccm[3][3];
++ __s32 offsets[3];
++};
++
++struct jh7110_isp_ccm_setting {
++ __u32 enabled;
++ struct jh7110_isp_ccm_smlow ccm_smlow;
++};
++
++struct jh7110_isp_cfa_params {
++ __s32 hv_width;
++ __s32 cross_cov;
++};
++
++struct jh7110_isp_cfa_setting {
++ __u32 enabled;
++ struct jh7110_isp_cfa_params settings;
++};
++
++struct jh7110_isp_ctc_params {
++ __u8 saf_mode;
++ __u8 daf_mode;
++ __s32 max_gt;
++ __s32 min_gt;
++};
++
++struct jh7110_isp_ctc_setting {
++ __u32 enabled;
++ struct jh7110_isp_ctc_params settings;
++};
++
++struct jh7110_isp_dbc_params {
++ __s32 bad_gt;
++ __s32 bad_xt;
++};
++
++struct jh7110_isp_dbc_setting {
++ __u32 enabled;
++ struct jh7110_isp_dbc_params settings;
++};
++
++struct jh7110_isp_dnyuv_params {
++ __u8 y_sweight[10];
++ __u16 y_curve[7];
++ __u8 uv_sweight[10];
++ __u16 uv_curve[7];
++};
++
++struct jh7110_isp_dnyuv_setting {
++ __u32 enabled;
++ struct jh7110_isp_dnyuv_params settings;
++};
++
++struct jh7110_isp_gmargb_point {
++ __u16 g_val;
++ __u16 sg_val;
++};
++
++struct jh7110_isp_gmargb_setting {
++ __u32 enabled;
++ struct jh7110_isp_gmargb_point curve[15];
++};
++
++struct jh7110_isp_lccf_circle {
++ __s16 center_x;
++ __s16 center_y;
++ __u8 radius;
++};
++
++struct jh7110_isp_lccf_curve_param {
++ __s16 f1;
++ __s16 f2;
++};
++
++struct jh7110_isp_lccf_setting {
++ __u32 enabled;
++ struct jh7110_isp_lccf_circle circle;
++ struct jh7110_isp_lccf_curve_param r_param;
++ struct jh7110_isp_lccf_curve_param gr_param;
++ struct jh7110_isp_lccf_curve_param gb_param;
++ struct jh7110_isp_lccf_curve_param b_param;
++};
++
++struct jh7110_isp_blacklevel_win_size {
++ __u32 width;
++ __u32 height;
++};
++
++struct jh7110_isp_blacklevel_gain {
++ __u8 tl_gain;
++ __u8 tr_gain;
++ __u8 bl_gain;
++ __u8 br_gain;
++};
++
++struct jh7110_isp_blacklevel_offset {
++ __u8 tl_offset;
++ __u8 tr_offset;
++ __u8 bl_offset;
++ __u8 br_offset;
++};
++
++struct jh7110_isp_blacklevel_setting {
++ __u32 enabled;
++ struct jh7110_isp_blacklevel_win_size win_size;
++ struct jh7110_isp_blacklevel_gain gain[4];
++ struct jh7110_isp_blacklevel_offset offset[4];
++};
++
++struct jh7110_isp_oecf_point {
++ __u16 x;
++ __u16 y;
++ __s16 slope;
++};
++
++struct jh7110_isp_oecf_setting {
++ __u32 enabled;
++ struct jh7110_isp_oecf_point r_curve[16];
++ struct jh7110_isp_oecf_point gr_curve[16];
++ struct jh7110_isp_oecf_point gb_curve[16];
++ struct jh7110_isp_oecf_point b_curve[16];
++};
++
++struct jh7110_isp_r2y_matrix {
++ __s16 m[9];
++};
++
++struct jh7110_isp_r2y_setting {
++ __u32 enabled;
++ struct jh7110_isp_r2y_matrix matrix;
++};
++
++struct jh7110_isp_sat_curve {
++ __s16 yi_min;
++ __s16 yo_ir;
++ __s16 yo_min;
++ __s16 yo_max;
++};
++
++struct jh7110_isp_sat_hue_info {
++ __s16 sin;
++ __s16 cos;
++};
++
++struct jh7110_isp_sat_info {
++ __s16 gain_cmab;
++ __s16 gain_cmad;
++ __s16 threshold_cmb;
++ __s16 threshold_cmd;
++ __s16 offset_u;
++ __s16 offset_v;
++ __s16 cmsf;
++};
++
++struct jh7110_isp_sat_setting {
++ __u32 enabled;
++ struct jh7110_isp_sat_curve curve;
++ struct jh7110_isp_sat_hue_info hue_info;
++ struct jh7110_isp_sat_info sat_info;
++};
++
++struct jh7110_isp_sharp_weight {
++ __u8 weight[15];
++ __u32 recip_wei_sum;
++};
++
++struct jh7110_isp_sharp_strength {
++ __s16 diff[4];
++ __s16 f[4];
++};
++
++struct jh7110_isp_sharp_setting {
++ __u32 enabled;
++ struct jh7110_isp_sharp_weight weight;
++ struct jh7110_isp_sharp_strength strength;
++ __s8 pdirf;
++ __s8 ndirf;
++};
++
++struct jh7110_isp_ycrv_curve {
++ __s16 y[64];
++};
++
++struct jh7110_isp_ycrv_setting {
++ __u32 enabled;
++ struct jh7110_isp_ycrv_curve curve;
++};
++
++#endif
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -203,6 +203,12 @@ enum v4l2_colorfx {
+ */
+ #define V4L2_CID_USER_ASPEED_BASE (V4L2_CID_USER_BASE + 0x11a0)
+
++/*
++ * The base for the jh7110-isp driver controls.
++ * We reserve 16 controls for this driver.
++ */
++#define V4L2_CID_USER_JH7110_ISP_BASE (V4L2_CID_USER_BASE + 0x1170)
++
+ /* MPEG-class control IDs */
+ /* The MPEG controls are applicable to all codec controls
+ * and the 'MPEG' part of the define is historical */
+--- /dev/null
++++ b/include/video/stf-vin.h
+@@ -0,0 +1,443 @@
++/* include/video/stf-vin.h
++ *
++ * Copyright 2020 starfive tech.
++ * Eric Tang <eric.tang@starfivetech.com>
++ *
++ * Generic vin notifier interface
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++*/
++#ifndef _VIDEO_VIN_H
++#define _VIDEO_VIN_H
++
++#include <linux/cdev.h>
++
++#define DRV_NAME "jh7110-vin"
++#define FB_FIRST_ADDR 0xf9000000
++#define FB_SECOND_ADDR 0xf97e9000
++
++#define RESERVED_MEM_SIZE 0x1000000
++
++#define VIN_MIPI_CONTROLLER0_OFFSET 0x00000
++#define VIN_CLKGEN_OFFSET 0x10000
++#define VIN_RSTGEN_OFFSET 0x20000
++#define VIN_MIPI_CONTROLLER1_OFFSET 0x30000
++#define VIN_SYSCONTROLLER_OFFSET 0x40000
++
++#define VD_1080P 1080
++#define VD_720P 720
++#define VD_PAL 480
++
++#define VD_HEIGHT_1080P VD_1080P
++#define VD_WIDTH_1080P 1920
++
++#define VD_HEIGHT_720P VD_720P
++#define VD_WIDTH_720P 1080
++
++#define VD_HEIGHT_480 480
++#define VD_WIDTH_640 640
++
++#define SEEED_WIDTH_800 800
++#define SEEED_HIGH_480 480
++
++#define VIN_TOP_CLKGEN_BASE_ADDR 0x11800000
++#define VIN_TOP_RSTGEN_BASE_ADDR 0x11840000
++#define VIN_TOP_IOPAD_BASE_ADDR 0x11858000
++
++#define ISP_BASE_MIPI0_ADDR 0x19800000
++#define ISP_BASE_CLKGEN_ADDR 0x19810000
++#define ISP_BASE_RSTGEN_ADDR 0x19820000
++#define ISP_BASE_MIPI1_ADDR 0x19830000
++#define ISP_BASE_SYSCTRL_ADDR 0x19840000
++#define ISP_BASE_ISP0_ADDR 0x19870000
++#define ISP_BASE_ISP1_ADDR 0x198a0000
++
++
++//vin clk registers
++#define CLK_VIN_SRC_CTRL 0x188
++#define CLK_ISP0_AXI_CTRL 0x190
++#define CLK_ISP0NOC_AXI_CTRL 0x194
++#define CLK_ISPSLV_AXI_CTRL 0x198
++#define CLK_ISP1_AXI_CTRL 0x1A0
++#define CLK_ISP1NOC_AXI_CTRL 0x1A4
++#define CLK_VIN_AXI 0x1AC
++#define CLK_VINNOC_AXI 0x1B0
++
++
++#define CLK_DOM4_APB_FUNC 0x0
++#define CLK_MUX_SEL 0xffffff
++
++#define CLK_MIPI_RX0_PXL 0x4
++
++#define CLK_DVP_INV 0x8
++#define CLK_U0_VIN_PCLK 0x18
++#define CLK_U0_VIN_PCLK_ICG (0x1<<31)
++
++#define CLK_U0_VIN_SYS_CLK 0x1c
++#define CLK_U0_VIN_CLK_P_AXIWR 0x30
++#define CLK_U0_VIN_MUX_SEL (BIT(24) | BIT(25) | BIT(26) | BIT(27) | BIT(28) | BIT(29))
++
++#define CLK_U0_VIN_PIXEL_CLK_IF0 0x20
++#define CLK_U0_VIN_PIXEL_CLK_IF1 0x24
++#define CLK_U0_VIN_PIXEL_CLK_IF2 0x28
++#define CLK_U0_VIN_PIXEL_CLK_IF3 0x2c
++
++#define CLK_U0_VIN_CLK_P_AXIWR 0x30
++
++#define CLK_U0_ISPV2_TOP_WRAPPER_CLK_C 0x34u
++#define CLK_U0_ISPV2_MUX_SEL (0x1<<24 | 0x1<<25 | 0x1<<26 | 0x1<<27 | 0x1<<28 | 0x1<< 29)
++
++#define CLK_U0_ISPV2_CLK_ICG (0x1<<31)
++
++#define SOFTWARE_RESET_ASSERT0_ASSERT_SET 0x38U
++#define SOFTWARE_RESET_ASSERT0_ASSERT_SET_STATE 0x3CU
++#define RST_U0_ISPV2_TOP_WRAPPER_RST_P BIT(0)
++#define RST_U0_ISPV2_TOP_WRAPPER_RST_C BIT(1)
++#define RSTN_U0_VIN_RST_N_PCLK BIT(4)
++#define RSTN_U0_VIN_RST_N_SYS_CLK BIT(9)
++#define RSTN_U0_VIN_RST_P_AXIRD BIT(10)
++#define RSTN_U0_VIN_RST_P_AXIWR BIT(11)
++
++
++#define CLK_POLARITY (0x1<<30)
++
++#define M31DPHY_APBCFGSAIF__SYSCFG_0 0x0
++#define M31DPHY_APBCFGSAIF__SYSCFG_4 0x4
++#define M31DPHY_APBCFGSAIF__SYSCFG_8 0x8
++#define M31DPHY_APBCFGSAIF__SYSCFG_12 0xc
++#define M31DPHY_APBCFGSAIF__SYSCFG_16 0x10
++#define M31DPHY_APBCFGSAIF__SYSCFG_20 0x14
++#define M31DPHY_APBCFGSAIF__SYSCFG_24 0x18
++#define M31DPHY_APBCFGSAIF__SYSCFG_28 0x1c
++#define M31DPHY_APBCFGSAIF__SYSCFG_32 0x20
++#define M31DPHY_APBCFGSAIF__SYSCFG_36 0x24
++#define M31DPHY_APBCFGSAIF__SYSCFG_40 0x28
++#define M31DPHY_APBCFGSAIF__SYSCFG_44 0x2c
++#define M31DPHY_APBCFGSAIF__SYSCFG_48 0x30
++#define M31DPHY_APBCFGSAIF__SYSCFG_52 0x34
++#define M31DPHY_APBCFGSAIF__SYSCFG_56 0x38
++#define M31DPHY_APBCFGSAIF__SYSCFG_60 0x3c
++#define M31DPHY_APBCFGSAIF__SYSCFG_64 0x40
++#define M31DPHY_APBCFGSAIF__SYSCFG_68 0x44
++#define M31DPHY_APBCFGSAIF__SYSCFG_72 0x48
++#define M31DPHY_APBCFGSAIF__SYSCFG_76 0x4c
++#define M31DPHY_APBCFGSAIF__SYSCFG_80 0x50
++#define M31DPHY_APBCFGSAIF__SYSCFG_84 0x54
++#define M31DPHY_APBCFGSAIF__SYSCFG_88 0x58
++#define M31DPHY_APBCFGSAIF__SYSCFG_92 0x5c
++#define M31DPHY_APBCFGSAIF__SYSCFG_96 0x60
++#define M31DPHY_APBCFGSAIF__SYSCFG_100 0x64
++#define M31DPHY_APBCFGSAIF__SYSCFG_104 0x68
++#define M31DPHY_APBCFGSAIF__SYSCFG_108 0x6c
++#define M31DPHY_APBCFGSAIF__SYSCFG_112 0x70
++#define M31DPHY_APBCFGSAIF__SYSCFG_116 0x74
++#define M31DPHY_APBCFGSAIF__SYSCFG_120 0x78
++#define M31DPHY_APBCFGSAIF__SYSCFG_124 0x7c
++#define M31DPHY_APBCFGSAIF__SYSCFG_128 0x80
++#define M31DPHY_APBCFGSAIF__SYSCFG_132 0x84
++#define M31DPHY_APBCFGSAIF__SYSCFG_136 0x88
++#define M31DPHY_APBCFGSAIF__SYSCFG_140 0x8c
++#define M31DPHY_APBCFGSAIF__SYSCFG_144 0x90
++#define M31DPHY_APBCFGSAIF__SYSCFG_184 0xb8
++
++//pmu registers
++#define SW_DEST_POWER_ON 0x0C
++#define SW_DEST_POWER_OFF 0x10
++#define SW_ENCOURAGE 0x44
++
++
++//isp clk registers
++#define CLK_DPHY_CFGCLK_ISPCORE_2X_CTRL 0x00
++#define CLK_DPHY_REFCLK_ISPCORE_2X_CTRL 0x04
++#define CLK_DPHY_TXCLKESC_IN_CTRL 0x08
++#define CLK_MIPI_RX0_PXL_CTRL 0x0c
++#define CLK_MIPI_RX1_PXL_CTRL 0x10
++#define CLK_MIPI_RX0_PXL_0_CTRL 0X14
++#define CLK_MIPI_RX0_PXL_1_CTRL 0X18
++#define CLK_MIPI_RX0_PXL_2_CTRL 0X1C
++#define CLK_MIPI_RX0_PXL_3_CTRL 0X20
++#define CLK_MIPI_RX0_SYS0_CTRL 0x24
++#define CLK_MIPI_RX1_PXL_0_CTRL 0X28
++#define CLK_MIPI_RX1_PXL_1_CTRL 0X2C
++#define CLK_MIPI_RX1_PXL_2_CTRL 0X30
++#define CLK_MIPI_RX1_PXL_3_CTRL 0X34
++#define CLK_MIPI_RX1_SYS1_CTRL 0x38
++#define CLK_ISP_CTRL 0x3c
++#define CLK_ISP_2X_CTRL 0x40
++#define CLK_ISP_MIPI_CTRL 0x44
++#define CLK_C_ISP_CTRL 0x64
++#define CLK_CSI2RX0_APB_CTRL 0x58
++
++
++#define CLK_VIN_AXI_WR_CTRL 0x5C
++
++#define SOFTWARE_RESET_ASSERT0 0x0
++#define SOFTWARE_RESET_ASSERT1 0x4
++#define SOFTWARE_RESET_STATUS 0x4
++
++#define IOPAD_REG81 0x144
++#define IOPAD_REG82 0x148
++#define IOPAD_REG83 0x14C
++#define IOPAD_REG84 0x150
++#define IOPAD_REG85 0x154
++#define IOPAD_REG86 0x158
++#define IOPAD_REG87 0x15C
++#define IOPAD_REG88 0x160
++#define IOPAD_REG89 0x164
++
++//sys control REG DEFINE
++#define SYSCONSAIF_SYSCFG_0 0X0
++#define U0_VIN_SCFG_SRAM_CONFIG (BIT(0) | BIT(1))
++
++#define SYSCONSAIF_SYSCFG_4 0x4
++#define U0_VIN_CNFG_AXIRD_END_ADDR 0xffffffff
++#define SYSCONSAIF_SYSCFG_8 0x8
++#define U0_VIN_CNFG_AXIRD_LINE_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
++#define U0_VIN_CNFG_AXIRD_LINE_CNT_START (BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
++#define SYSCONSAIF_SYSCFG_12 0xc
++#define U0_VIN_CNFG_AXIRD_PIX_CNT_END (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIRD_PIX_CNT_START (BIT(13) | BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
++#define U0_VIN_CNFG_AXIRD_PIX_CT (BIT(26) | BIT(27))
++#define SYSCONSAIF_SYSCFG_16 0x10
++#define U0_VIN_CNFG_AXIRD_START_ADDR 0xFFFFFFFF
++#define SYSCONSAIF_SYSCFG_20 0x14
++#define U0_VIN_CNFG_AXIWR0_EN BIT(4)
++#define U0_VIN_CNFG_AXIWR0_CHANNEL_SEL (BIT(0) | BIT(1) | BIT(2) | BIT(3))
++#define SYSCONSAIF_SYSCFG_24 0x18
++#define U0_VIN_CNFG_AXIWR0_END_ADDR 0xFFFFFFFF
++
++#define SYSCONSAIF_SYSCFG_28 0x1c
++#define U0_VIN_CNFG_AXIWR0_INTR_CLEAN BIT(0)
++#define U0_VIN_CNFG_AXIWR0_MASK BIT(1)
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIWR0_PIX_CT (BIT(13) | BIT(14))
++#define UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL (BIT(15) | BIT(16))
++#define SYSCONSAIF_SYSCFG_32 0x20
++
++#define SYSCONSAIF_SYSCFG_36 0x24
++#define UO_VIN_CNFG_COLOR_BAR_EN BIT(0)
++#define U0_VIN_CNFG_DVP_HS_POS (0x1<<1)
++#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2)
++#define U0_VIN_CNFG_DVP_VS_POS (0x1<<3)
++#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4)
++#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5)
++#define U0_VIN_CNFG_MIPI_BYTE_EN_ISP0 (BIT(6) |BIT(7))
++#define U0_VIN_CNFG_P_I_MIPI_CHANNEL_SEL0 (BIT(8) |BIT(9) | BIT(10) | BIT(11))
++#define U0_VIN_CNFG_P_I_MIPI_HEADER_EN0 BIT(12)
++
++#define U0_VIN_CNFG_PIX_NUM (0x1<<13 | 0x1<<14 | 0x1<<15 | 0x1<<16)
++#define U0_VIN_CNFG_AXIRD_AXI_CNT_END (BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
++
++#define U0_VIN_CNFG_AXI_DVP_EN BIT(2)
++#define U0_VIN_CNFG_AXIRD_INTR_MASK BIT(1)
++#define U0_VIN_CNFG_AXIWRD_INTR_MASK BIT(1)
++#define U0_VIN_CNFG_AXIWR0_START_ADDR 0xffffffff
++#define U0_VIN_CNFG_COLOR_BAR_EN 0X0
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CT (BIT(13) | BIT(14))
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIWR0_PIXEL_HITH_BIT_SEL (BIT(15) | BIT(16))
++
++#define SYSCTRL_REG4 0x10
++#define SYSCTRL_DPHY_CTRL 0x14
++#define SYSCTRL_VIN_AXI_CTRL 0x18
++#define SYSCTRL_VIN_WR_START_ADDR 0x28
++#define SYSCTRL_VIN_RD_END_ADDR 0x2C
++#define SYSCTRL_VIN_WR_PIX_TOTAL 0x30
++#define SYSCTRL_VIN_RD_PIX_TOTAL 0x34
++#define SYSCTRL_VIN_RW_CTRL 0x38
++#define SYSCTRL_VIN_SRC_CHAN_SEL 0x24
++#define SYSCTRL_VIN_SRC_DW_SEL 0x40
++#define SYSCTRL_VIN_RD_VBLANK 0x44
++#define SYSCTRL_VIN_RD_VEND 0x48
++#define SYSCTRL_VIN_RD_HBLANK 0x4C
++#define SYSCTRL_VIN_RD_HEND 0x50
++#define SYSCTRL_VIN_INTP_CTRL 0x54
++
++#define ISP_NO_SCALE_ENABLE (0x1<<20)
++#define ISP_MULTI_FRAME_ENABLE (0x1<<17)
++#define ISP_SS0_ENABLE (0x1<<11)
++#define ISP_SS1_ENABLE (0x1<<12)
++#define ISP_RESET (0x1<<1)
++#define ISP_ENBALE (0x1)
++
++
++
++ //ISP REG DEFINE
++#define ISP_REG_DVP_POLARITY_CFG 0x00000014
++#define ISP_REG_RAW_FORMAT_CFG 0x00000018
++#define ISP_REG_CFA_MODE 0x00000A1C
++#define ISP_REG_PIC_CAPTURE_START_CFG 0x0000001C
++#define ISP_REG_PIC_CAPTURE_END_CFG 0x00000020
++#define ISP_REG_PIPELINE_XY_SIZE 0x00000A0C
++#define ISP_REG_Y_PLANE_START_ADDR 0x00000A80
++#define ISP_REG_UV_PLANE_START_ADDR 0x00000A84
++#define ISP_REG_STRIDE 0x00000A88
++#define ISP_REG_PIXEL_COORDINATE_GEN 0x00000A8C
++#define ISP_REG_PIXEL_AXI_CONTROL 0x00000A90
++#define ISP_REG_SS_AXI_CONTROL 0x00000AC4
++#define ISP_REG_RGB_TO_YUV_COVERSION0 0x00000E40
++#define ISP_REG_RGB_TO_YUV_COVERSION1 0x00000E44
++#define ISP_REG_RGB_TO_YUV_COVERSION2 0x00000E48
++#define ISP_REG_RGB_TO_YUV_COVERSION3 0x00000E4C
++#define ISP_REG_RGB_TO_YUV_COVERSION4 0x00000E50
++#define ISP_REG_RGB_TO_YUV_COVERSION5 0x00000E54
++#define ISP_REG_RGB_TO_YUV_COVERSION6 0x00000E58
++#define ISP_REG_RGB_TO_YUV_COVERSION7 0x00000E5C
++#define ISP_REG_RGB_TO_YUV_COVERSION8 0x00000E60
++#define ISP_REG_CSI_MODULE_CFG 0x00000010
++#define ISP_REG_ISP_CTRL_1 0x00000A08
++#define ISP_REG_ISP_CTRL_0 0x00000A00
++#define ISP_REG_DC_AXI_ID 0x00000044
++#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x00000000
++
++//CSI registers
++#define DEVICE_CONFIG 0x00
++#define SOFT_RESET 0x04
++#define STATIC_CFG 0x08
++#define ERROR_BYPASS_CFG 0x10
++#define MONITOR_IRQS 0x18
++#define MONITOR_IRQS_MASK_CFG 0x1c
++#define INFO_IRQS 0x20
++#define INFO_IRQS_MASK_CFG 0x24
++#define ERROR_IRQS 0x28
++#define ERROR_IRQS_MASK_CFG 0x2c
++#define DPHY_LANE_CONTROL 0x40
++#define DPHY_STATUS 0x48
++#define DPHY_ERR_STATUS_IRQ 0x4C
++#define DPHY_ERR_IRQ_MASK_CFG 0x50
++#define INTEGRATION_DEBUG 0x60
++#define ERROR_DEBUG 0x74
++
++#define STREAM0_CTRL 0x100
++#define STREAM0_STATUS 0x104
++#define STREAM0_DATA_CFG 0x108
++#define STREAM0_CFG 0x10c
++#define STREAM0_MONITOR_CTRL 0x110
++#define STREAM0_MONITOR_FRAME 0x114
++#define STREAM0_MONITOR_LB 0x118
++#define STREAM0_TIMER 0x11c
++#define STREAM0_FCC_CFG 0x120
++#define STREAM0_FCC_CTRL 0x124
++#define STREAM0_FIFO_FILL_LVL 0x128
++
++//m31_dphy registers
++#define M31DPHY_APBCFGSAIF__SYSCFG_188 0xbc
++#define M31DPHY_APBCFGSAIF__SYSCFG_192 0xc0
++#define M31DPHY_APBCFGSAIF__SYSCFG_196 0xc4
++#define M31DPHY_APBCFGSAIF__SYSCFG_200 0xc8
++
++typedef enum
++{
++ DT_RAW6 = 0x28,
++ DT_RAW7 = 0x29,
++ DT_RAW8 = 0x2a,
++ DT_RAW10 = 0x2b,
++ DT_RAW12 = 0x2c,
++ DT_RAW14 = 0x2d,
++} mipicam_data_type_t;
++
++
++enum VIN_SOURCE_FORMAT {
++ SRC_COLORBAR_VIN_ISP = 0,
++ SRC_DVP_SENSOR_VIN,
++ SRC_DVP_SENSOR_VIN_ISP,//need replace sensor
++ SRC_CSI2RX_VIN_ISP,
++ SRC_DVP_SENSOR_VIN_OV5640,
++};
++
++struct reg_name {
++ char name[10];
++};
++
++typedef struct
++{
++ int dlane_nb;
++ int dlane_map[4];
++ int dlane_en[4];
++ int dlane_pn_swap[4];
++ int clane_nb;
++ int clane_map[2];
++ int clane_pn_swap[2];
++} csi2rx_dphy_cfg_t;
++
++typedef struct
++{
++ int lane_nb;
++ int dlane_map[4];
++ int dt;
++ int hsize;
++ int vsize;
++} csi2rx_cfg_t;
++
++
++typedef struct
++{
++ int mipi_id, w, h, dt, bpp, fps,lane;
++ u8 clane_swap;
++ u8 clane_pn_swap;
++ u8 dlane_swap[4];
++ u8 dlane_pn_swap[4];
++} csi_format;
++
++struct vin_params {
++ void *paddr;
++ unsigned long size;
++};
++
++struct vin_buf {
++ void *vaddr;
++ dma_addr_t paddr;
++ u32 size;
++};
++
++struct vin_framesize {
++ u32 width;
++ u32 height;
++};
++
++struct vin_format {
++ enum VIN_SOURCE_FORMAT format;
++ u8 fps;
++};
++
++struct stf_vin_dev {
++ /* Protects the access of variables shared within the interrupt */
++ spinlock_t irqlock;
++ int irq;
++ struct device *dev;
++ struct cdev vin_cdev;
++ void __iomem *base;
++ void __iomem *csi2rx_base;
++ void __iomem *clkgen_base;
++ void __iomem *rstgen_base;
++ void __iomem *sysctrl_base;
++ void __iomem *isp_base;
++ void __iomem *vin_top_clkgen_base;
++ void __iomem *vin_top_rstgen_base;
++ void __iomem *vin_top_iopad_base;
++ void __iomem *pmu_test;
++ void __iomem *sys_crg;
++ struct vin_framesize frame;
++ struct vin_format format;
++ bool isp;
++ int isp_irq;
++ int isp_csi_irq;
++ int isp_scd_irq;
++ int isp_irq_csiline;
++ u32 major;
++ struct vin_buf buf;
++
++ wait_queue_head_t wq;
++ bool condition;
++ int odd;
++
++ csi_format csi_fmt;
++};
++
++extern int vin_notifier_register(struct notifier_block *nb);
++extern void vin_notifier_unregister(struct notifier_block *nb);
++extern int vin_notifier_call(unsigned long e, void *v);
++#endif
--- /dev/null
+From baec350a0994467584e7e390746e0bb365957a89 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Tue, 13 Jun 2023 16:55:23 +0800
+Subject: [PATCH 086/116] dt-bindings: media: i2c: Add IMX708 CMOS sensor
+ binding
+
+Add YAML devicetree binding for IMX708 CMOS image sensor.
+Let's also add a MAINTAINERS entry for the binding and driver.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../devicetree/bindings/media/i2c/imx708.yaml | 117 ++++++++++++++++++
+ 1 file changed, 117 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/imx708.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/imx708.yaml
+@@ -0,0 +1,117 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/imx708.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor
++
++maintainers:
++ - Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.om>
++
++description: |-
++ The Sony IMX708 is a 1/2.3-inch CMOS active pixel digital mage sensor
++ with an active array size of 4608H x 2592V. It is rogrammable through
++ I2C interface. The I2C address is fixed to 0x1A as per ensor data sheet.
++ Image data is sent through MIPI CSI-2, which is configured s either 2 or
++ 4 data lanes.
++
++properties:
++ compatible:
++ const: sony,imx708
++
++ reg:
++ description: I2C device address
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ VDIG-supply:
++ description:
++ Digital I/O voltage supply, 1.1 volts
++
++ VANA1-supply:
++ description:
++ Analog1 voltage supply, 2.8 volts
++
++ VANA2-supply:
++ description:
++ Analog2 voltage supply, 1.8 volts
++
++ VDDL-supply:
++ description:
++ Digital core voltage supply, 1.8 volts
++
++ reset-gpios:
++ description: |-
++ Reference to the GPIO connected to the xclr pin, if any.
++ Must be released (set high) after all supplies and INCK re applied.
++
++ # See ../video-interfaces.txt for more details
++ port:
++ type: object
++ properties:
++ endpoint:
++ type: object
++ properties:
++ data-lanes:
++ description: |-
++ The sensor supports either two-lane, or our-lane operation.
++ For two-lane operation the property must be set o <1 2>.
++ items:
++ - const: 1
++ - const: 2
++
++ clock-noncontinuous:
++ type: boolean
++ description: |-
++ MIPI CSI-2 clock is non-continuous if this roperty is present,
++ otherwise it's continuous.
++
++ link-frequencies:
++ allOf:
++ - $ref: /schemas/types.yaml#/definitions/int64-array
++ description:
++ Allowed data bus frequencies.
++
++ required:
++ - link-frequencies
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - VANA1-supply
++ - VANA2-supply
++ - VDIG-supply
++ - VDDL-supply
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ i2c0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ imx708: sensor@1a {
++ compatible = "sony,imx708";
++ reg = <0x1a>;
++ clocks = <&imx708_clk>;
++ VANA1-supply = <&imx708_vana1>; /* 1.8v */
++ VANA2-supply = <&imx708_vana2>; /* 2.8v */
++ VDIG-supply = <&imx708_vdig>; /* 1.1v */
++ VDDL-supply = <&imx708_vddl>; /* 1.8v */
++
++ port {
++ imx708_0: endpoint {
++ remote-endpoint = <&csi1_ep>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <450000000>;
++ };
++ };
++ };
++ };
--- /dev/null
+From e89556802c5d29a80f5887995ab257d4e826a90f Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 3 Apr 2023 13:52:17 +0800
+Subject: [PATCH 087/116] media: i2c: Add imx708 support
+
+Add imx708 support.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/i2c/Kconfig | 13 +
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/imx708.c | 1921 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1935 insertions(+)
+ create mode 100644 drivers/media/i2c/imx708.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -201,6 +201,19 @@ config VIDEO_IMX415
+ To compile this driver as a module, choose M here: the
+ module will be called imx415.
+
++config VIDEO_IMX708
++ tristate "Sony IMX708 sensor support"
++ depends on I2C && VIDEO_DEV
++ select MEDIA_CONTROLLER
++ select VIDEO_V4L2_SUBDEV_API
++ select V4L2_FWNODE
++ help
++ This is a Video4Linux2 sensor driver for the Sony
++ IMX708 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called imx708.
++
+ config VIDEO_MAX9271_LIB
+ tristate
+
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
+ obj-$(CONFIG_VIDEO_IMX355) += imx355.o
+ obj-$(CONFIG_VIDEO_IMX412) += imx412.o
+ obj-$(CONFIG_VIDEO_IMX415) += imx415.o
++obj-$(CONFIG_VIDEO_IMX708) += imx708.o
+ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+ obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
+ obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
+--- /dev/null
++++ b/drivers/media/i2c/imx708.c
+@@ -0,0 +1,1921 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX708 cameras.
++ * Copyright (C) 2022-2023, Raspberry Pi Ltd
++ *
++ * Based on Sony imx477 camera driver
++ * Copyright (C) 2020 Raspberry Pi Ltd
++ */
++#include <asm/unaligned.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++
++#define IMX708_REG_VALUE_08BIT 1
++#define IMX708_REG_VALUE_16BIT 2
++
++/* Chip ID */
++#define IMX708_REG_CHIP_ID 0x0016
++#define IMX708_CHIP_ID 0x0708
++
++#define IMX708_REG_MODE_SELECT 0x0100
++#define IMX708_MODE_STANDBY 0x00
++#define IMX708_MODE_STREAMING 0x01
++
++#define IMX708_REG_ORIENTATION 0x101
++
++#define IMX708_XCLK_FREQ 24000000
++
++#define IMX708_DEFAULT_LINK_FREQ 450000000
++
++/* V_TIMING internal */
++#define IMX708_REG_FRAME_LENGTH 0x0340
++#define IMX708_FRAME_LENGTH_MAX 0xffff
++
++/* Long exposure multiplier */
++#define IMX708_LONG_EXP_SHIFT_MAX 7
++#define IMX708_LONG_EXP_SHIFT_REG 0x3100
++
++/* Exposure control */
++#define IMX708_REG_EXPOSURE 0x0202
++#define IMX708_EXPOSURE_OFFSET 48
++#define IMX708_EXPOSURE_DEFAULT 0x640
++#define IMX708_EXPOSURE_STEP 1
++#define IMX708_EXPOSURE_MIN 1
++#define IMX708_EXPOSURE_MAX (IMX708_FRAME_LENGTH_MAX - \
++ IMX708_EXPOSURE_OFFSET)
++
++/* Analog gain control */
++#define IMX708_REG_ANALOG_GAIN 0x0204
++#define IMX708_ANA_GAIN_MIN 112
++#define IMX708_ANA_GAIN_MAX 960
++#define IMX708_ANA_GAIN_STEP 1
++#define IMX708_ANA_GAIN_DEFAULT IMX708_ANA_GAIN_MIN
++
++/* Digital gain control */
++#define IMX708_REG_DIGITAL_GAIN 0x020e
++#define IMX708_DGTL_GAIN_MIN 0x0100
++#define IMX708_DGTL_GAIN_MAX 0xffff
++#define IMX708_DGTL_GAIN_DEFAULT 0x0100
++#define IMX708_DGTL_GAIN_STEP 1
++
++/* Colour balance controls */
++#define IMX708_REG_COLOUR_BALANCE_RED 0x0b90
++#define IMX708_REG_COLOUR_BALANCE_BLUE 0x0b92
++#define IMX708_COLOUR_BALANCE_MIN 0x01
++#define IMX708_COLOUR_BALANCE_MAX 0xffff
++#define IMX708_COLOUR_BALANCE_STEP 0x01
++#define IMX708_COLOUR_BALANCE_DEFAULT 0x100
++
++/* Test Pattern Control */
++#define IMX708_REG_TEST_PATTERN 0x0600
++#define IMX708_TEST_PATTERN_DISABLE 0
++#define IMX708_TEST_PATTERN_SOLID_COLOR 1
++#define IMX708_TEST_PATTERN_COLOR_BARS 2
++#define IMX708_TEST_PATTERN_GREY_COLOR 3
++#define IMX708_TEST_PATTERN_PN9 4
++
++/* Test pattern colour components */
++#define IMX708_REG_TEST_PATTERN_R 0x0602
++#define IMX708_REG_TEST_PATTERN_GR 0x0604
++#define IMX708_REG_TEST_PATTERN_B 0x0606
++#define IMX708_REG_TEST_PATTERN_GB 0x0608
++#define IMX708_TEST_PATTERN_COLOUR_MIN 0
++#define IMX708_TEST_PATTERN_COLOUR_MAX 0x0fff
++#define IMX708_TEST_PATTERN_COLOUR_STEP 1
++
++#define IMX708_REG_BASE_SPC_GAINS_L 0x7b10
++#define IMX708_REG_BASE_SPC_GAINS_R 0x7c00
++
++/* HDR exposure ratio (long:med == med:short) */
++#define IMX708_HDR_EXPOSURE_RATIO 4
++#define IMX708_REG_MID_EXPOSURE 0x3116
++#define IMX708_REG_SHT_EXPOSURE 0x0224
++#define IMX708_REG_MID_ANALOG_GAIN 0x3118
++#define IMX708_REG_SHT_ANALOG_GAIN 0x0216
++
++/* IMX708 native and active pixel array size. */
++#define IMX708_NATIVE_WIDTH 4640U
++#define IMX708_NATIVE_HEIGHT 2658U
++#define IMX708_PIXEL_ARRAY_LEFT 16U
++#define IMX708_PIXEL_ARRAY_TOP 24U
++#define IMX708_PIXEL_ARRAY_WIDTH 4608U
++#define IMX708_PIXEL_ARRAY_HEIGHT 2592U
++
++struct imx708_reg {
++ u16 address;
++ u8 val;
++};
++
++struct imx708_reg_list {
++ unsigned int num_of_regs;
++ const struct imx708_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx708_mode {
++ /* Frame width */
++ unsigned int width;
++
++ /* Frame height */
++ unsigned int height;
++
++ /* H-timing in pixels */
++ unsigned int line_length_pix;
++
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
++ /* Highest possible framerate. */
++ unsigned int vblank_min;
++
++ /* Default framerate. */
++ unsigned int vblank_default;
++
++ /* Default register values */
++ struct imx708_reg_list reg_list;
++
++ /* Not all modes have the same pixel rate. */
++ u64 pixel_rate;
++
++ /* Not all modes have the same minimum exposure. */
++ u32 exposure_lines_min;
++
++ /* Not all modes have the same exposure lines step. */
++ u32 exposure_lines_step;
++
++ /* HDR flag, currently not used at runtime */
++ bool hdr;
++};
++
++/* Default PDAF pixel correction gains */
++static const u8 pdaf_gains[2][9] = {
++ { 0x4c, 0x4c, 0x4c, 0x46, 0x3e, 0x38, 0x35, 0x35, 0x35 },
++ { 0x35, 0x35, 0x35, 0x38, 0x3e, 0x46, 0x4c, 0x4c, 0x4c }
++};
++
++static const struct imx708_reg mode_common_regs[] = {
++ {0x0100, 0x00},
++ {0x0136, 0x18},
++ {0x0137, 0x00},
++ {0x33f0, 0x02},
++ {0x33f1, 0x05},
++ {0x3062, 0x00},
++ {0x3063, 0x12},
++ {0x3068, 0x00},
++ {0x3069, 0x12},
++ {0x306a, 0x00},
++ {0x306b, 0x30},
++ {0x3076, 0x00},
++ {0x3077, 0x30},
++ {0x3078, 0x00},
++ {0x3079, 0x30},
++ {0x5e54, 0x0c},
++ {0x6e44, 0x00},
++ {0xb0b6, 0x01},
++ {0xe829, 0x00},
++ {0xf001, 0x08},
++ {0xf003, 0x08},
++ {0xf00d, 0x10},
++ {0xf00f, 0x10},
++ {0xf031, 0x08},
++ {0xf033, 0x08},
++ {0xf03d, 0x10},
++ {0xf03f, 0x10},
++ {0x0112, 0x0a},
++ {0x0113, 0x0a},
++ {0x0114, 0x01},
++ {0x0b8e, 0x01},
++ {0x0b8f, 0x00},
++ {0x0b94, 0x01},
++ {0x0b95, 0x00},
++ {0x3400, 0x01},
++ {0x3478, 0x01},
++ {0x3479, 0x1c},
++ {0x3091, 0x01},
++ {0x3092, 0x00},
++ {0x3419, 0x00},
++ {0xbcf1, 0x02},
++ {0x3094, 0x01},
++ {0x3095, 0x01},
++ {0x3362, 0x00},
++ {0x3363, 0x00},
++ {0x3364, 0x00},
++ {0x3365, 0x00},
++ {0x0138, 0x01},
++};
++
++/* 10-bit. */
++static const struct imx708_reg mode_4608x2592_regs[] = {
++ {0x0342, 0x3d},
++ {0x0343, 0x20},
++ {0x0340, 0x0a},
++ {0x0341, 0x59},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x11},
++ {0x0349, 0xff},
++ {0x034a, 0x0a},
++ {0x034b, 0x1f},
++ {0x0220, 0x62},
++ {0x0222, 0x01},
++ {0x0900, 0x00},
++ {0x0901, 0x11},
++ {0x0902, 0x0a},
++ {0x3200, 0x01},
++ {0x3201, 0x01},
++ {0x32d5, 0x01},
++ {0x32d6, 0x00},
++ {0x32db, 0x01},
++ {0x32df, 0x00},
++ {0x350c, 0x00},
++ {0x350d, 0x00},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x12},
++ {0x040d, 0x00},
++ {0x040e, 0x0a},
++ {0x040f, 0x20},
++ {0x034c, 0x12},
++ {0x034d, 0x00},
++ {0x034e, 0x0a},
++ {0x034f, 0x20},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x02},
++ {0x0306, 0x00},
++ {0x0307, 0x7c},
++ {0x030b, 0x02},
++ {0x030d, 0x04},
++ {0x030e, 0x01},
++ {0x030f, 0x2c},
++ {0x0310, 0x01},
++ {0x3ca0, 0x00},
++ {0x3ca1, 0x64},
++ {0x3ca4, 0x00},
++ {0x3ca5, 0x00},
++ {0x3ca6, 0x00},
++ {0x3ca7, 0x00},
++ {0x3caa, 0x00},
++ {0x3cab, 0x00},
++ {0x3cb8, 0x00},
++ {0x3cb9, 0x08},
++ {0x3cba, 0x00},
++ {0x3cbb, 0x00},
++ {0x3cbc, 0x00},
++ {0x3cbd, 0x3c},
++ {0x3cbe, 0x00},
++ {0x3cbf, 0x00},
++ {0x0202, 0x0a},
++ {0x0203, 0x29},
++ {0x0224, 0x01},
++ {0x0225, 0xf4},
++ {0x3116, 0x01},
++ {0x3117, 0xf4},
++ {0x0204, 0x00},
++ {0x0205, 0x00},
++ {0x0216, 0x00},
++ {0x0217, 0x00},
++ {0x0218, 0x01},
++ {0x0219, 0x00},
++ {0x020e, 0x01},
++ {0x020f, 0x00},
++ {0x3118, 0x00},
++ {0x3119, 0x00},
++ {0x311a, 0x01},
++ {0x311b, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x01},
++ {0x341f, 0x20},
++ {0x3420, 0x00},
++ {0x3421, 0xd8},
++ {0xc428, 0x00},
++ {0xc429, 0x04},
++ {0x3366, 0x00},
++ {0x3367, 0x00},
++ {0x3368, 0x00},
++ {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_2x2binned_regs[] = {
++ {0x0342, 0x1e},
++ {0x0343, 0x90},
++ {0x0340, 0x05},
++ {0x0341, 0x38},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x11},
++ {0x0349, 0xff},
++ {0x034a, 0x0a},
++ {0x034b, 0x1f},
++ {0x0220, 0x62},
++ {0x0222, 0x01},
++ {0x0900, 0x01},
++ {0x0901, 0x22},
++ {0x0902, 0x08},
++ {0x3200, 0x41},
++ {0x3201, 0x41},
++ {0x32d5, 0x00},
++ {0x32d6, 0x00},
++ {0x32db, 0x01},
++ {0x32df, 0x00},
++ {0x350c, 0x00},
++ {0x350d, 0x00},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x09},
++ {0x040d, 0x00},
++ {0x040e, 0x05},
++ {0x040f, 0x10},
++ {0x034c, 0x09},
++ {0x034d, 0x00},
++ {0x034e, 0x05},
++ {0x034f, 0x10},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x02},
++ {0x0306, 0x00},
++ {0x0307, 0x7a},
++ {0x030b, 0x02},
++ {0x030d, 0x04},
++ {0x030e, 0x01},
++ {0x030f, 0x2c},
++ {0x0310, 0x01},
++ {0x3ca0, 0x00},
++ {0x3ca1, 0x3c},
++ {0x3ca4, 0x00},
++ {0x3ca5, 0x3c},
++ {0x3ca6, 0x00},
++ {0x3ca7, 0x00},
++ {0x3caa, 0x00},
++ {0x3cab, 0x00},
++ {0x3cb8, 0x00},
++ {0x3cb9, 0x1c},
++ {0x3cba, 0x00},
++ {0x3cbb, 0x08},
++ {0x3cbc, 0x00},
++ {0x3cbd, 0x1e},
++ {0x3cbe, 0x00},
++ {0x3cbf, 0x0a},
++ {0x0202, 0x05},
++ {0x0203, 0x08},
++ {0x0224, 0x01},
++ {0x0225, 0xf4},
++ {0x3116, 0x01},
++ {0x3117, 0xf4},
++ {0x0204, 0x00},
++ {0x0205, 0x70},
++ {0x0216, 0x00},
++ {0x0217, 0x70},
++ {0x0218, 0x01},
++ {0x0219, 0x00},
++ {0x020e, 0x01},
++ {0x020f, 0x00},
++ {0x3118, 0x00},
++ {0x3119, 0x70},
++ {0x311a, 0x01},
++ {0x311b, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x90},
++ {0x3420, 0x00},
++ {0x3421, 0x6c},
++ {0x3366, 0x00},
++ {0x3367, 0x00},
++ {0x3368, 0x00},
++ {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_2x2binned_720p_regs[] = {
++ {0x0342, 0x14},
++ {0x0343, 0x60},
++ {0x0340, 0x04},
++ {0x0341, 0xb6},
++ {0x0344, 0x03},
++ {0x0345, 0x00},
++ {0x0346, 0x01},
++ {0x0347, 0xb0},
++ {0x0348, 0x0e},
++ {0x0349, 0xff},
++ {0x034a, 0x08},
++ {0x034b, 0x6f},
++ {0x0220, 0x62},
++ {0x0222, 0x01},
++ {0x0900, 0x01},
++ {0x0901, 0x22},
++ {0x0902, 0x08},
++ {0x3200, 0x41},
++ {0x3201, 0x41},
++ {0x32d5, 0x00},
++ {0x32d6, 0x00},
++ {0x32db, 0x01},
++ {0x32df, 0x01},
++ {0x350c, 0x00},
++ {0x350d, 0x00},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x06},
++ {0x040d, 0x00},
++ {0x040e, 0x03},
++ {0x040f, 0x60},
++ {0x034c, 0x06},
++ {0x034d, 0x00},
++ {0x034e, 0x03},
++ {0x034f, 0x60},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x02},
++ {0x0306, 0x00},
++ {0x0307, 0x76},
++ {0x030b, 0x02},
++ {0x030d, 0x04},
++ {0x030e, 0x01},
++ {0x030f, 0x2c},
++ {0x0310, 0x01},
++ {0x3ca0, 0x00},
++ {0x3ca1, 0x3c},
++ {0x3ca4, 0x01},
++ {0x3ca5, 0x5e},
++ {0x3ca6, 0x00},
++ {0x3ca7, 0x00},
++ {0x3caa, 0x00},
++ {0x3cab, 0x00},
++ {0x3cb8, 0x00},
++ {0x3cb9, 0x0c},
++ {0x3cba, 0x00},
++ {0x3cbb, 0x04},
++ {0x3cbc, 0x00},
++ {0x3cbd, 0x1e},
++ {0x3cbe, 0x00},
++ {0x3cbf, 0x05},
++ {0x0202, 0x04},
++ {0x0203, 0x86},
++ {0x0224, 0x01},
++ {0x0225, 0xf4},
++ {0x3116, 0x01},
++ {0x3117, 0xf4},
++ {0x0204, 0x00},
++ {0x0205, 0x70},
++ {0x0216, 0x00},
++ {0x0217, 0x70},
++ {0x0218, 0x01},
++ {0x0219, 0x00},
++ {0x020e, 0x01},
++ {0x020f, 0x00},
++ {0x3118, 0x00},
++ {0x3119, 0x70},
++ {0x311a, 0x01},
++ {0x311b, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x60},
++ {0x3420, 0x00},
++ {0x3421, 0x48},
++ {0x3366, 0x00},
++ {0x3367, 0x00},
++ {0x3368, 0x00},
++ {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_hdr_regs[] = {
++ {0x0342, 0x14},
++ {0x0343, 0x60},
++ {0x0340, 0x0a},
++ {0x0341, 0x5b},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x11},
++ {0x0349, 0xff},
++ {0x034a, 0x0a},
++ {0x034b, 0x1f},
++ {0x0220, 0x01},
++ {0x0222, IMX708_HDR_EXPOSURE_RATIO},
++ {0x0900, 0x00},
++ {0x0901, 0x11},
++ {0x0902, 0x0a},
++ {0x3200, 0x01},
++ {0x3201, 0x01},
++ {0x32d5, 0x00},
++ {0x32d6, 0x00},
++ {0x32db, 0x01},
++ {0x32df, 0x00},
++ {0x350c, 0x00},
++ {0x350d, 0x00},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x09},
++ {0x040d, 0x00},
++ {0x040e, 0x05},
++ {0x040f, 0x10},
++ {0x034c, 0x09},
++ {0x034d, 0x00},
++ {0x034e, 0x05},
++ {0x034f, 0x10},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x02},
++ {0x0306, 0x00},
++ {0x0307, 0xa2},
++ {0x030b, 0x02},
++ {0x030d, 0x04},
++ {0x030e, 0x01},
++ {0x030f, 0x2c},
++ {0x0310, 0x01},
++ {0x3ca0, 0x00},
++ {0x3ca1, 0x00},
++ {0x3ca4, 0x00},
++ {0x3ca5, 0x00},
++ {0x3ca6, 0x00},
++ {0x3ca7, 0x28},
++ {0x3caa, 0x00},
++ {0x3cab, 0x00},
++ {0x3cb8, 0x00},
++ {0x3cb9, 0x30},
++ {0x3cba, 0x00},
++ {0x3cbb, 0x00},
++ {0x3cbc, 0x00},
++ {0x3cbd, 0x32},
++ {0x3cbe, 0x00},
++ {0x3cbf, 0x00},
++ {0x0202, 0x0a},
++ {0x0203, 0x2b},
++ {0x0224, 0x0a},
++ {0x0225, 0x2b},
++ {0x3116, 0x0a},
++ {0x3117, 0x2b},
++ {0x0204, 0x00},
++ {0x0205, 0x00},
++ {0x0216, 0x00},
++ {0x0217, 0x00},
++ {0x0218, 0x01},
++ {0x0219, 0x00},
++ {0x020e, 0x01},
++ {0x020f, 0x00},
++ {0x3118, 0x00},
++ {0x3119, 0x00},
++ {0x311a, 0x01},
++ {0x311b, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x90},
++ {0x3420, 0x00},
++ {0x3421, 0x6c},
++ {0x3360, 0x01},
++ {0x3361, 0x01},
++ {0x3366, 0x09},
++ {0x3367, 0x00},
++ {0x3368, 0x05},
++ {0x3369, 0x10},
++};
++
++/* Mode configs. Keep separate lists for when HDR is enabled or not. */
++static const struct imx708_mode supported_modes_10bit_no_hdr[] = {
++ {
++ /* Full resolution. */
++ .width = 4608,
++ .height = 2592,
++ .line_length_pix = 0x3d20,
++ .crop = {
++ .left = IMX708_PIXEL_ARRAY_LEFT,
++ .top = IMX708_PIXEL_ARRAY_TOP,
++ .width = 4608,
++ .height = 2592,
++ },
++ .vblank_min = 58,
++ .vblank_default = 58,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_4608x2592_regs),
++ .regs = mode_4608x2592_regs,
++ },
++ .pixel_rate = 595200000,
++ .exposure_lines_min = 8,
++ .exposure_lines_step = 1,
++ .hdr = false
++ },
++ {
++ /* regular 2x2 binned. */
++ .width = 2304,
++ .height = 1296,
++ .line_length_pix = 0x1e90,
++ .crop = {
++ .left = IMX708_PIXEL_ARRAY_LEFT,
++ .top = IMX708_PIXEL_ARRAY_TOP,
++ .width = 4608,
++ .height = 2592,
++ },
++ .vblank_min = 40,
++ .vblank_default = 1198,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_2x2binned_regs),
++ .regs = mode_2x2binned_regs,
++ },
++ .pixel_rate = 585600000,
++ .exposure_lines_min = 4,
++ .exposure_lines_step = 2,
++ .hdr = false
++ },
++ {
++ /* 2x2 binned and cropped for 720p. */
++ .width = 1536,
++ .height = 864,
++ .line_length_pix = 0x1460,
++ .crop = {
++ .left = IMX708_PIXEL_ARRAY_LEFT + 768,
++ .top = IMX708_PIXEL_ARRAY_TOP + 432,
++ .width = 3072,
++ .height = 1728,
++ },
++ .vblank_min = 40,
++ .vblank_default = 2755,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_2x2binned_720p_regs),
++ .regs = mode_2x2binned_720p_regs,
++ },
++ .pixel_rate = 566400000,
++ .exposure_lines_min = 4,
++ .exposure_lines_step = 2,
++ .hdr = false
++ },
++};
++
++static const struct imx708_mode supported_modes_10bit_hdr[] = {
++ {
++ /* There's only one HDR mode, which is 2x2 downscaled */
++ .width = 2304,
++ .height = 1296,
++ .line_length_pix = 0x1460,
++ .crop = {
++ .left = IMX708_PIXEL_ARRAY_LEFT,
++ .top = IMX708_PIXEL_ARRAY_TOP,
++ .width = 4608,
++ .height = 2592,
++ },
++ .vblank_min = 3673,
++ .vblank_default = 3673,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_hdr_regs),
++ .regs = mode_hdr_regs,
++ },
++ .pixel_rate = 777600000,
++ .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO *
++ IMX708_HDR_EXPOSURE_RATIO,
++ .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO *
++ IMX708_HDR_EXPOSURE_RATIO,
++ .hdr = true
++ }
++};
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++ /* 10-bit modes. */
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++};
++
++static const char * const imx708_test_pattern_menu[] = {
++ "Disabled",
++ "Color Bars",
++ "Solid Color",
++ "Grey Color Bars",
++ "PN9"
++};
++
++static const int imx708_test_pattern_val[] = {
++ IMX708_TEST_PATTERN_DISABLE,
++ IMX708_TEST_PATTERN_COLOR_BARS,
++ IMX708_TEST_PATTERN_SOLID_COLOR,
++ IMX708_TEST_PATTERN_GREY_COLOR,
++ IMX708_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx708_supply_name[] = {
++ /* Supplies can be enabled in any order */
++ "VANA1", /* Analog1 (2.8V) supply */
++ "VANA2", /* Analog2 (1.8V) supply */
++ "VDIG", /* Digital Core (1.1V) supply */
++ "VDDL", /* IF (1.8V) supply */
++};
++
++#define IMX708_NUM_SUPPLIES ARRAY_SIZE(imx708_supply_name)
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software standby), given by T7 in the
++ * datasheet is 8ms. This does include I2C setup time as well.
++ *
++ * Note, that delay between XCLR low->high and reading the CCI ID register (T6
++ * in the datasheet) is much smaller - 600us.
++ */
++#define IMX708_XCLR_MIN_DELAY_US 8000
++#define IMX708_XCLR_DELAY_RANGE_US 1000
++
++struct imx708 {
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++
++ struct v4l2_mbus_framefmt fmt;
++
++ struct clk *xclk;
++ u32 xclk_freq;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[IMX708_NUM_SUPPLIES];
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ /* V4L2 Controls */
++ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++ struct v4l2_ctrl *red_balance;
++ struct v4l2_ctrl *blue_balance;
++ struct v4l2_ctrl *notify_gains;
++ struct v4l2_ctrl *hdr_mode;
++
++ /* Current mode */
++ const struct imx708_mode *mode;
++
++ /* Mutex for serialized access */
++ struct mutex mutex;
++
++ /* Streaming on/off */
++ bool streaming;
++
++ /* Rewrite common registers on stream on? */
++ bool common_regs_written;
++
++ /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
++ unsigned int long_exp_shift;
++};
++
++static inline struct imx708 *to_imx708(struct v4l2_subdev *_sd)
++{
++ return container_of(_sd, struct imx708, sd);
++}
++
++static inline void get_mode_table(const struct imx708_mode **mode_list,
++ unsigned int *num_modes,
++ bool hdr_enable)
++{
++ if (hdr_enable) {
++ *mode_list = supported_modes_10bit_hdr;
++ *num_modes = ARRAY_SIZE(supported_modes_10bit_hdr);
++ } else {
++ *mode_list = supported_modes_10bit_no_hdr;
++ *num_modes = ARRAY_SIZE(supported_modes_10bit_no_hdr);
++ }
++}
++
++/* Read registers up to 2 at a time */
++static int imx708_read_reg(struct imx708 *imx708, u16 reg, u32 len, u32 *val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ struct i2c_msg msgs[2];
++ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++ u8 data_buf[4] = { 0, };
++ int ret;
++
++ if (len > 4)
++ return -EINVAL;
++
++ /* Write register address */
++ msgs[0].addr = client->addr;
++ msgs[0].flags = 0;
++ msgs[0].len = ARRAY_SIZE(addr_buf);
++ msgs[0].buf = addr_buf;
++
++ /* Read data from register */
++ msgs[1].addr = client->addr;
++ msgs[1].flags = I2C_M_RD;
++ msgs[1].len = len;
++ msgs[1].buf = &data_buf[4 - len];
++
++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++ if (ret != ARRAY_SIZE(msgs))
++ return -EIO;
++
++ *val = get_unaligned_be32(data_buf);
++
++ return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx708_write_reg(struct imx708 *imx708, u16 reg, u32 len, u32 val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ u8 buf[6];
++
++ if (len > 4)
++ return -EINVAL;
++
++ put_unaligned_be16(reg, buf);
++ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++ if (i2c_master_send(client, buf, len + 2) != len + 2)
++ return -EIO;
++
++ return 0;
++}
++
++/* Write a list of registers */
++static int imx708_write_regs(struct imx708 *imx708,
++ const struct imx708_reg *regs, u32 len)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < len; i++) {
++ ret = imx708_write_reg(imx708, regs[i].address, 1, regs[i].val);
++ if (ret) {
++ dev_err_ratelimited(&client->dev,
++ "Failed to write reg 0x%4.4x. error = %d\n",
++ regs[i].address, ret);
++
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx708_get_format_code(struct imx708 *imx708)
++{
++ unsigned int i;
++
++ lockdep_assert_held(&imx708->mutex);
++
++ i = (imx708->vflip->val ? 2 : 0) |
++ (imx708->hflip->val ? 1 : 0);
++
++ return codes[i];
++}
++
++static void imx708_set_default_format(struct imx708 *imx708)
++{
++ struct v4l2_mbus_framefmt *fmt = &imx708->fmt;
++
++ /* Set default mode to max resolution */
++ imx708->mode = &supported_modes_10bit_no_hdr[0];
++
++ /* fmt->code not set as it will always be computed based on flips */
++ fmt->colorspace = V4L2_COLORSPACE_RAW;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = imx708->mode->width;
++ fmt->height = imx708->mode->height;
++ fmt->field = V4L2_FIELD_NONE;
++}
++
++static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ struct imx708 *imx708 = to_imx708(sd);
++ struct v4l2_mbus_framefmt *try_fmt_img =
++ v4l2_subdev_get_try_format(sd, fh->state, 0);
++ struct v4l2_rect *try_crop;
++
++ mutex_lock(&imx708->mutex);
++
++ /* Initialize try_fmt for the image pad */
++ if (imx708->hdr_mode->val) {
++ try_fmt_img->width = supported_modes_10bit_hdr[0].width;
++ try_fmt_img->height = supported_modes_10bit_hdr[0].height;
++ } else {
++ try_fmt_img->width = supported_modes_10bit_no_hdr[0].width;
++ try_fmt_img->height = supported_modes_10bit_no_hdr[0].height;
++ }
++ try_fmt_img->code = imx708_get_format_code(imx708);
++ try_fmt_img->field = V4L2_FIELD_NONE;
++
++ /* Initialize try_crop */
++ try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0);
++ try_crop->left = IMX708_PIXEL_ARRAY_LEFT;
++ try_crop->top = IMX708_PIXEL_ARRAY_TOP;
++ try_crop->width = IMX708_PIXEL_ARRAY_WIDTH;
++ try_crop->height = IMX708_PIXEL_ARRAY_HEIGHT;
++
++ mutex_unlock(&imx708->mutex);
++
++ return 0;
++}
++
++static int imx708_set_exposure(struct imx708 *imx708, unsigned int val)
++{
++ int ret;
++
++ val = max(val, imx708->mode->exposure_lines_min);
++ val -= val % imx708->mode->exposure_lines_step;
++
++ /*
++ * In HDR mode this will set the longest exposure. The sensor
++ * will automatically divide the medium and short ones by 4,16.
++ */
++ ret = imx708_write_reg(imx708, IMX708_REG_EXPOSURE,
++ IMX708_REG_VALUE_16BIT,
++ val >> imx708->long_exp_shift);
++
++ return ret;
++}
++
++static void imx708_adjust_exposure_range(struct imx708 *imx708,
++ struct v4l2_ctrl *ctrl)
++{
++ int exposure_max, exposure_def;
++
++ /* Honour the VBLANK limits when setting exposure. */
++ exposure_max = imx708->mode->height + imx708->vblank->val -
++ IMX708_EXPOSURE_OFFSET;
++ exposure_def = min(exposure_max, imx708->exposure->val);
++ __v4l2_ctrl_modify_range(imx708->exposure, imx708->exposure->minimum,
++ exposure_max, imx708->exposure->step,
++ exposure_def);
++}
++
++static int imx708_set_analogue_gain(struct imx708 *imx708, unsigned int val)
++{
++ int ret;
++
++ /*
++ * In HDR mode this will set the gain for the longest exposure,
++ * and by default the sensor uses the same gain for all of them.
++ */
++ ret = imx708_write_reg(imx708, IMX708_REG_ANALOG_GAIN,
++ IMX708_REG_VALUE_16BIT, val);
++
++ return ret;
++}
++
++static int imx708_set_frame_length(struct imx708 *imx708, unsigned int val)
++{
++ int ret = 0;
++
++ imx708->long_exp_shift = 0;
++
++ while (val > IMX708_FRAME_LENGTH_MAX) {
++ imx708->long_exp_shift++;
++ val >>= 1;
++ }
++
++ ret = imx708_write_reg(imx708, IMX708_REG_FRAME_LENGTH,
++ IMX708_REG_VALUE_16BIT, val);
++ if (ret)
++ return ret;
++
++ return imx708_write_reg(imx708, IMX708_LONG_EXP_SHIFT_REG,
++ IMX708_REG_VALUE_08BIT, imx708->long_exp_shift);
++}
++
++static void imx708_set_framing_limits(struct imx708 *imx708)
++{
++ unsigned int hblank;
++ const struct imx708_mode *mode = imx708->mode;
++
++ /* Default to no long exposure multiplier */
++ imx708->long_exp_shift = 0;
++
++ __v4l2_ctrl_modify_range(imx708->pixel_rate,
++ mode->pixel_rate, mode->pixel_rate,
++ 1, mode->pixel_rate);
++
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(imx708->vblank, mode->vblank_min,
++ ((1 << IMX708_LONG_EXP_SHIFT_MAX) *
++ IMX708_FRAME_LENGTH_MAX) - mode->height,
++ 1, mode->vblank_default);
++
++ /*
++ * Currently PPL is fixed to the mode specified value, so hblank
++ * depends on mode->width only, and is not changeable in any
++ * way other than changing the mode.
++ */
++ hblank = mode->line_length_pix - mode->width;
++ __v4l2_ctrl_modify_range(imx708->hblank, hblank, hblank, 1, hblank);
++}
++
++static int imx708_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct imx708 *imx708 =
++ container_of(ctrl->handler, struct imx708, ctrl_handler);
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ const struct imx708_mode *mode_list;
++ unsigned int num_modes;
++ int ret;
++
++ /*
++ * The VBLANK control may change the limits of usable exposure, so check
++ * and adjust if necessary.
++ */
++ if (ctrl->id == V4L2_CID_VBLANK)
++ imx708_adjust_exposure_range(imx708, ctrl);
++
++ /*
++ * Applying V4L2 control value only happens
++ * when power is up for streaming
++ */
++ if (!pm_runtime_get_if_in_use(&client->dev))
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = imx708_set_analogue_gain(imx708, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = imx708_set_exposure(imx708, ctrl->val);
++ break;
++ case V4L2_CID_DIGITAL_GAIN:
++ ret = imx708_write_reg(imx708, IMX708_REG_DIGITAL_GAIN,
++ IMX708_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN,
++ IMX708_REG_VALUE_16BIT,
++ imx708_test_pattern_val[ctrl->val]);
++ break;
++ case V4L2_CID_TEST_PATTERN_RED:
++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_R,
++ IMX708_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENR:
++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GR,
++ IMX708_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_BLUE:
++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_B,
++ IMX708_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENB:
++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GB,
++ IMX708_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ ret = imx708_write_reg(imx708, IMX708_REG_ORIENTATION, 1,
++ imx708->hflip->val |
++ imx708->vflip->val << 1);
++ break;
++ case V4L2_CID_VBLANK:
++ ret = imx708_set_frame_length(imx708,
++ imx708->mode->height + ctrl->val);
++ break;
++ case V4L2_CID_NOTIFY_GAINS:
++ ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_BLUE,
++ IMX708_REG_VALUE_16BIT,
++ imx708->notify_gains->p_new.p_u32[0]);
++ if (ret)
++ break;
++ ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_RED,
++ IMX708_REG_VALUE_16BIT,
++ imx708->notify_gains->p_new.p_u32[3]);
++ break;
++ case V4L2_CID_WIDE_DYNAMIC_RANGE:
++ get_mode_table(&mode_list, &num_modes, ctrl->val);
++ imx708->mode = v4l2_find_nearest_size(mode_list,
++ num_modes,
++ width, height,
++ imx708->mode->width,
++ imx708->mode->height);
++ imx708_set_framing_limits(imx708);
++ ret = 0;
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n",
++ ctrl->id, ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops imx708_ctrl_ops = {
++ .s_ctrl = imx708_set_ctrl,
++};
++
++static int imx708_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct imx708 *imx708 = to_imx708(sd);
++
++ if (code->index >= 1)
++ return -EINVAL;
++
++ code->code = imx708_get_format_code(imx708);
++
++ return 0;
++}
++
++static int imx708_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct imx708 *imx708 = to_imx708(sd);
++ const struct imx708_mode *mode_list;
++ unsigned int num_modes;
++
++ get_mode_table(&mode_list, &num_modes, imx708->hdr_mode->val);
++
++ if (fse->index >= num_modes)
++ return -EINVAL;
++
++ if (fse->code != imx708_get_format_code(imx708))
++ return -EINVAL;
++
++ fse->min_width = mode_list[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = mode_list[fse->index].height;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static void imx708_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++ fmt->colorspace = V4L2_COLORSPACE_RAW;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx708_update_image_pad_format(struct imx708 *imx708,
++ const struct imx708_mode *mode,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = mode->width;
++ fmt->format.height = mode->height;
++ fmt->format.field = V4L2_FIELD_NONE;
++ imx708_reset_colorspace(&fmt->format);
++}
++
++static int imx708_get_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx708 *imx708 = to_imx708(sd);
++
++ mutex_lock(&imx708->mutex);
++
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(&imx708->sd, sd_state,
++ fmt->pad);
++ /* update the code which could change due to vflip or hflip */
++ try_fmt->code = imx708_get_format_code(imx708);
++ fmt->format = *try_fmt;
++ } else {
++ imx708_update_image_pad_format(imx708, imx708->mode, fmt);
++ fmt->format.code = imx708_get_format_code(imx708);
++ }
++
++ mutex_unlock(&imx708->mutex);
++ return 0;
++}
++
++static int imx708_set_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct v4l2_mbus_framefmt *framefmt;
++ const struct imx708_mode *mode;
++ struct imx708 *imx708 = to_imx708(sd);
++ const struct imx708_mode *mode_list;
++ unsigned int num_modes;
++
++ mutex_lock(&imx708->mutex);
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx708_get_format_code(imx708);
++
++ get_mode_table(&mode_list, &num_modes, imx708->hdr_mode->val);
++
++ mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
++ fmt->format.width, fmt->format.height);
++ imx708_update_image_pad_format(imx708, mode, fmt);
++
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
++ *framefmt = fmt->format;
++ } else {
++ imx708->mode = mode;
++ imx708_set_framing_limits(imx708);
++ }
++
++ mutex_unlock(&imx708->mutex);
++
++ return 0;
++}
++
++static const struct v4l2_rect *
++__imx708_get_pad_crop(struct imx708 *imx708, struct v4l2_subdev_state *sd_state,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&imx708->sd, sd_state, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &imx708->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int imx708_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct imx708 *imx708 = to_imx708(sd);
++
++ mutex_lock(&imx708->mutex);
++ sel->r = *__imx708_get_pad_crop(imx708, sd_state, sel->pad,
++ sel->which);
++ mutex_unlock(&imx708->mutex);
++
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.left = 0;
++ sel->r.top = 0;
++ sel->r.width = IMX708_NATIVE_WIDTH;
++ sel->r.height = IMX708_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ case V4L2_SEL_TGT_CROP_BOUNDS:
++ sel->r.left = IMX708_PIXEL_ARRAY_LEFT;
++ sel->r.top = IMX708_PIXEL_ARRAY_TOP;
++ sel->r.width = IMX708_PIXEL_ARRAY_WIDTH;
++ sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++/* Start streaming */
++static int imx708_start_streaming(struct imx708 *imx708)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ const struct imx708_reg_list *reg_list;
++ int i, ret;
++ u32 val;
++
++ if (!imx708->common_regs_written) {
++ ret = imx708_write_regs(imx708, mode_common_regs,
++ ARRAY_SIZE(mode_common_regs));
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set common settings\n",
++ __func__);
++ return ret;
++ }
++
++ ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L,
++ IMX708_REG_VALUE_08BIT, &val);
++ if (ret == 0 && val == 0x40) {
++ for (i = 0; i < 54 && ret == 0; i++) {
++ u16 reg = IMX708_REG_BASE_SPC_GAINS_L + i;
++
++ ret = imx708_write_reg(imx708, reg,
++ IMX708_REG_VALUE_08BIT,
++ pdaf_gains[0][i % 9]);
++ }
++ for (i = 0; i < 54 && ret == 0; i++) {
++ u16 reg = IMX708_REG_BASE_SPC_GAINS_R + i;
++
++ ret = imx708_write_reg(imx708, reg,
++ IMX708_REG_VALUE_08BIT,
++ pdaf_gains[1][i % 9]);
++ }
++ }
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set PDAF gains\n",
++ __func__);
++ return ret;
++ }
++
++ imx708->common_regs_written = true;
++ }
++
++ /* Apply default values of current mode */
++ reg_list = &imx708->mode->reg_list;
++ ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set mode\n", __func__);
++ return ret;
++ }
++
++ /* Apply customized values from user */
++ ret = __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler);
++ if (ret)
++ return ret;
++
++ /* set stream on register */
++ return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT,
++ IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING);
++}
++
++/* Stop streaming */
++static void imx708_stop_streaming(struct imx708 *imx708)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ int ret;
++
++ /* set stream off register */
++ ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT,
++ IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY);
++ if (ret)
++ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++}
++
++static int imx708_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct imx708 *imx708 = to_imx708(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ mutex_lock(&imx708->mutex);
++ if (imx708->streaming == enable) {
++ mutex_unlock(&imx708->mutex);
++ return 0;
++ }
++
++ if (enable) {
++ ret = pm_runtime_resume_and_get(&client->dev);
++ if (ret < 0)
++ goto err_unlock;
++
++ /*
++ * Apply default & customized values
++ * and then start streaming.
++ */
++ ret = imx708_start_streaming(imx708);
++ if (ret)
++ goto err_rpm_put;
++ } else {
++ imx708_stop_streaming(imx708);
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++ }
++
++ imx708->streaming = enable;
++
++ /* vflip/hflip and hdr mode cannot change during streaming */
++ __v4l2_ctrl_grab(imx708->vflip, enable);
++ __v4l2_ctrl_grab(imx708->hflip, enable);
++ __v4l2_ctrl_grab(imx708->hdr_mode, enable);
++
++ mutex_unlock(&imx708->mutex);
++
++ return ret;
++
++err_rpm_put:
++ pm_runtime_put_sync(&client->dev);
++err_unlock:
++ mutex_unlock(&imx708->mutex);
++
++ return ret;
++}
++
++/* Power/clock management functions */
++static int imx708_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx708 *imx708 = to_imx708(sd);
++ int ret;
++
++ ret = regulator_bulk_enable(IMX708_NUM_SUPPLIES,
++ imx708->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ return ret;
++ }
++
++ ret = clk_prepare_enable(imx708->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ goto reg_off;
++ }
++
++ gpiod_set_value_cansleep(imx708->reset_gpio, 1);
++ usleep_range(IMX708_XCLR_MIN_DELAY_US,
++ IMX708_XCLR_MIN_DELAY_US + IMX708_XCLR_DELAY_RANGE_US);
++
++ return 0;
++
++reg_off:
++ regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies);
++ return ret;
++}
++
++static int imx708_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx708 *imx708 = to_imx708(sd);
++
++ gpiod_set_value_cansleep(imx708->reset_gpio, 0);
++ clk_disable_unprepare(imx708->xclk);
++ regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies);
++
++ /* Force reprogramming of the common registers when powered up again. */
++ imx708->common_regs_written = false;
++
++ return 0;
++}
++
++static int __maybe_unused imx708_suspend(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx708 *imx708 = to_imx708(sd);
++
++ if (imx708->streaming)
++ imx708_stop_streaming(imx708);
++
++ return 0;
++}
++
++static int __maybe_unused imx708_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx708 *imx708 = to_imx708(sd);
++ int ret;
++
++ if (imx708->streaming) {
++ ret = imx708_start_streaming(imx708);
++ if (ret)
++ goto error;
++ }
++
++ return 0;
++
++error:
++ imx708_stop_streaming(imx708);
++ imx708->streaming = 0;
++ return ret;
++}
++
++static int imx708_get_regulators(struct imx708 *imx708)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ unsigned int i;
++
++ for (i = 0; i < IMX708_NUM_SUPPLIES; i++)
++ imx708->supplies[i].supply = imx708_supply_name[i];
++
++ return devm_regulator_bulk_get(&client->dev,
++ IMX708_NUM_SUPPLIES,
++ imx708->supplies);
++}
++
++/* Verify chip ID */
++static int imx708_identify_module(struct imx708 *imx708)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ int ret;
++ u32 val;
++
++ ret = imx708_read_reg(imx708, IMX708_REG_CHIP_ID,
++ IMX708_REG_VALUE_16BIT, &val);
++ if (ret) {
++ dev_err(&client->dev, "failed to read chip id %x, with error %d\n",
++ IMX708_CHIP_ID, ret);
++ return ret;
++ }
++
++ if (val != IMX708_CHIP_ID) {
++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++ IMX708_CHIP_ID, val);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx708_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx708_video_ops = {
++ .s_stream = imx708_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx708_pad_ops = {
++ .enum_mbus_code = imx708_enum_mbus_code,
++ .get_fmt = imx708_get_pad_format,
++ .set_fmt = imx708_set_pad_format,
++ .get_selection = imx708_get_selection,
++ .enum_frame_size = imx708_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx708_subdev_ops = {
++ .core = &imx708_core_ops,
++ .video = &imx708_video_ops,
++ .pad = &imx708_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx708_internal_ops = {
++ .open = imx708_open,
++};
++
++static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = {
++ .ops = &imx708_ctrl_ops,
++ .id = V4L2_CID_NOTIFY_GAINS,
++ .type = V4L2_CTRL_TYPE_U32,
++ .min = IMX708_COLOUR_BALANCE_MIN,
++ .max = IMX708_COLOUR_BALANCE_MAX,
++ .step = IMX708_COLOUR_BALANCE_STEP,
++ .def = IMX708_COLOUR_BALANCE_DEFAULT,
++ .dims = { 4 },
++ .elem_size = sizeof(u32),
++};
++
++/* Initialize control handlers */
++static int imx708_init_controls(struct imx708 *imx708)
++{
++ struct v4l2_ctrl_handler *ctrl_hdlr;
++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++ struct v4l2_fwnode_device_properties props;
++ unsigned int i;
++ int ret;
++
++ ctrl_hdlr = &imx708->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
++ if (ret)
++ return ret;
++
++ mutex_init(&imx708->mutex);
++ ctrl_hdlr->lock = &imx708->mutex;
++
++ /* By default, PIXEL_RATE is read only */
++ imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_PIXEL_RATE,
++ imx708->mode->pixel_rate,
++ imx708->mode->pixel_rate, 1,
++ imx708->mode->pixel_rate);
++
++ /*
++ * Create the controls here, but mode specific limits are setup
++ * in the imx708_set_framing_limits() call below.
++ */
++ imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
++ imx708->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
++
++ imx708->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ IMX708_EXPOSURE_MIN,
++ IMX708_EXPOSURE_MAX,
++ IMX708_EXPOSURE_STEP,
++ IMX708_EXPOSURE_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ IMX708_ANA_GAIN_MIN, IMX708_ANA_GAIN_MAX,
++ IMX708_ANA_GAIN_STEP, IMX708_ANA_GAIN_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++ IMX708_DGTL_GAIN_MIN, IMX708_DGTL_GAIN_MAX,
++ IMX708_DGTL_GAIN_STEP, IMX708_DGTL_GAIN_DEFAULT);
++
++ imx708->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++
++ imx708->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++
++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(imx708_test_pattern_menu) - 1,
++ 0, 0, imx708_test_pattern_menu);
++ for (i = 0; i < 4; i++) {
++ /*
++ * The assumption is that
++ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2
++ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++ */
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_TEST_PATTERN_RED + i,
++ IMX708_TEST_PATTERN_COLOUR_MIN,
++ IMX708_TEST_PATTERN_COLOUR_MAX,
++ IMX708_TEST_PATTERN_COLOUR_STEP,
++ IMX708_TEST_PATTERN_COLOUR_MAX);
++ /* The "Solid color" pattern is white by default */
++ }
++
++ imx708->notify_gains = v4l2_ctrl_new_custom(ctrl_hdlr,
++ &imx708_notify_gains_ctrl,
++ NULL);
++
++ imx708->hdr_mode = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++ V4L2_CID_WIDE_DYNAMIC_RANGE,
++ 0, 1, 1, 0);
++
++ ret = v4l2_fwnode_device_parse(&client->dev, &props);
++ if (ret)
++ goto error;
++
++ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx708_ctrl_ops, &props);
++
++ if (ctrl_hdlr->error) {
++ ret = ctrl_hdlr->error;
++ dev_err(&client->dev, "%s control init failed (%d)\n",
++ __func__, ret);
++ goto error;
++ }
++
++ imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ imx708->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++ imx708->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++ imx708->hdr_mode->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ imx708->sd.ctrl_handler = ctrl_hdlr;
++
++ /* Setup exposure and frame/line length limits. */
++ imx708_set_framing_limits(imx708);
++
++ return 0;
++
++error:
++ v4l2_ctrl_handler_free(ctrl_hdlr);
++ mutex_destroy(&imx708->mutex);
++
++ return ret;
++}
++
++static void imx708_free_controls(struct imx708 *imx708)
++{
++ v4l2_ctrl_handler_free(imx708->sd.ctrl_handler);
++ mutex_destroy(&imx708->mutex);
++}
++
++static int imx708_check_hwcfg(struct device *dev)
++{
++ struct fwnode_handle *endpoint;
++ struct v4l2_fwnode_endpoint ep_cfg = {
++ .bus_type = V4L2_MBUS_CSI2_DPHY
++ };
++ int ret = -EINVAL;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++ dev_err(dev, "could not parse endpoint\n");
++ goto error_out;
++ }
++
++ /* Check the number of MIPI CSI2 data lanes */
++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(dev, "only 2 data lanes are currently supported\n");
++ goto error_out;
++ }
++
++ /* Check the link frequency set in device tree */
++ if (!ep_cfg.nr_of_link_frequencies) {
++ dev_err(dev, "link-frequency property not found in DT\n");
++ goto error_out;
++ }
++
++ if (ep_cfg.nr_of_link_frequencies != 1 ||
++ ep_cfg.link_frequencies[0] != IMX708_DEFAULT_LINK_FREQ) {
++ dev_err(dev, "Link frequency not supported: %lld\n",
++ ep_cfg.link_frequencies[0]);
++ goto error_out;
++ }
++
++ ret = 0;
++
++error_out:
++ v4l2_fwnode_endpoint_free(&ep_cfg);
++ fwnode_handle_put(endpoint);
++
++ return ret;
++}
++
++static int imx708_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct imx708 *imx708;
++ int ret;
++
++ imx708 = devm_kzalloc(&client->dev, sizeof(*imx708), GFP_KERNEL);
++ if (!imx708)
++ return -ENOMEM;
++
++ v4l2_i2c_subdev_init(&imx708->sd, client, &imx708_subdev_ops);
++
++ /* Check the hardware configuration in device tree */
++ if (imx708_check_hwcfg(dev))
++ return -EINVAL;
++
++ /* Get system clock (xclk) */
++ imx708->xclk = devm_clk_get(dev, NULL);
++ if (IS_ERR(imx708->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(imx708->xclk);
++ }
++
++ imx708->xclk_freq = clk_get_rate(imx708->xclk);
++ if (imx708->xclk_freq != IMX708_XCLK_FREQ) {
++ dev_err(dev, "xclk frequency not supported: %d Hz\n",
++ imx708->xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = imx708_get_regulators(imx708);
++ if (ret) {
++ dev_err(dev, "failed to get regulators\n");
++ return ret;
++ }
++
++ /* Request optional enable pin */
++ imx708->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++
++ /*
++ * The sensor must be powered for imx708_identify_module()
++ * to be able to read the CHIP_ID register
++ */
++ ret = imx708_power_on(dev);
++ if (ret)
++ return ret;
++
++ ret = imx708_identify_module(imx708);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize default format */
++ imx708_set_default_format(imx708);
++
++ /*
++ * Enable runtime PM with autosuspend. As the device has been powered
++ * manually, mark it as active, and increase the usage count without
++ * resuming the device.
++ */
++ pm_runtime_set_active(dev);
++ pm_runtime_get_noresume(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_set_autosuspend_delay(dev, 1000);
++ pm_runtime_use_autosuspend(dev);
++
++ /* This needs the pm runtime to be registered. */
++ ret = imx708_init_controls(imx708);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize subdev */
++ imx708->sd.internal_ops = &imx708_internal_ops;
++ imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ imx708->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ /* Initialize source pad */
++ imx708->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&imx708->sd.entity, 1, &imx708->pad);
++ if (ret) {
++ dev_err(dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ret = v4l2_async_register_subdev_sensor(&imx708->sd);
++ if (ret < 0) {
++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ /*
++ * Decrease the PM usage count. The device will get suspended after the
++ * autosuspend delay, turning the power off.
++ */
++ pm_runtime_mark_last_busy(dev);
++ pm_runtime_put_autosuspend(dev);
++
++ return 0;
++
++error_media_entity:
++ media_entity_cleanup(&imx708->sd.entity);
++
++error_handler_free:
++ imx708_free_controls(imx708);
++
++error_power_off:
++ pm_runtime_disable(&client->dev);
++ pm_runtime_put_noidle(&client->dev);
++ imx708_power_off(&client->dev);
++
++ return ret;
++}
++
++static void imx708_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx708 *imx708 = to_imx708(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ imx708_free_controls(imx708);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ imx708_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id imx708_dt_ids[] = {
++ { .compatible = "sony,imx708" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx708_dt_ids);
++
++static const struct dev_pm_ops imx708_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume)
++ SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL)
++};
++
++static struct i2c_driver imx708_i2c_driver = {
++ .driver = {
++ .name = "imx708",
++ .of_match_table = imx708_dt_ids,
++ .pm = &imx708_pm_ops,
++ },
++ .probe = imx708_probe,
++ .remove = imx708_remove,
++};
++
++module_i2c_driver(imx708_i2c_driver);
++
++MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>");
++MODULE_DESCRIPTION("Sony IMX708 sensor driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 280ab217867d0b934454a837540eab665e854b47 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 3 Apr 2023 15:28:11 +0800
+Subject: [PATCH 088/116] media: i2c: imx708: Delete gain
+
+Delete gain.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/i2c/imx708.c | 27 ---------------------------
+ 1 file changed, 27 deletions(-)
+
+--- a/drivers/media/i2c/imx708.c
++++ b/drivers/media/i2c/imx708.c
+@@ -777,7 +777,6 @@ struct imx708 {
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+- struct v4l2_ctrl *notify_gains;
+ struct v4l2_ctrl *hdr_mode;
+
+ /* Current mode */
+@@ -1108,16 +1107,6 @@ static int imx708_set_ctrl(struct v4l2_c
+ ret = imx708_set_frame_length(imx708,
+ imx708->mode->height + ctrl->val);
+ break;
+- case V4L2_CID_NOTIFY_GAINS:
+- ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_BLUE,
+- IMX708_REG_VALUE_16BIT,
+- imx708->notify_gains->p_new.p_u32[0]);
+- if (ret)
+- break;
+- ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_RED,
+- IMX708_REG_VALUE_16BIT,
+- imx708->notify_gains->p_new.p_u32[3]);
+- break;
+ case V4L2_CID_WIDE_DYNAMIC_RANGE:
+ get_mode_table(&mode_list, &num_modes, ctrl->val);
+ imx708->mode = v4l2_find_nearest_size(mode_list,
+@@ -1584,18 +1573,6 @@ static const struct v4l2_subdev_internal
+ .open = imx708_open,
+ };
+
+-static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = {
+- .ops = &imx708_ctrl_ops,
+- .id = V4L2_CID_NOTIFY_GAINS,
+- .type = V4L2_CTRL_TYPE_U32,
+- .min = IMX708_COLOUR_BALANCE_MIN,
+- .max = IMX708_COLOUR_BALANCE_MAX,
+- .step = IMX708_COLOUR_BALANCE_STEP,
+- .def = IMX708_COLOUR_BALANCE_DEFAULT,
+- .dims = { 4 },
+- .elem_size = sizeof(u32),
+-};
+-
+ /* Initialize control handlers */
+ static int imx708_init_controls(struct imx708 *imx708)
+ {
+@@ -1670,10 +1647,6 @@ static int imx708_init_controls(struct i
+ /* The "Solid color" pattern is white by default */
+ }
+
+- imx708->notify_gains = v4l2_ctrl_new_custom(ctrl_hdlr,
+- &imx708_notify_gains_ctrl,
+- NULL);
+-
+ imx708->hdr_mode = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
+ V4L2_CID_WIDE_DYNAMIC_RANGE,
+ 0, 1, 1, 0);
--- /dev/null
+From aa4febf074cbaad81c981a5c6b55324a6f676fb7 Mon Sep 17 00:00:00 2001
+From: "shengyang.chen" <shengyang.chen@starfivetech.com>
+Date: Tue, 13 Jun 2023 14:22:29 +0800
+Subject: [PATCH 089/116] dt-bindings: display: Add yamls for JH7110 display
+ system and hdmi
+
+StarFive SoCs like the jh7110 use display system based on verisilicon IP, use hdmi
+base on innosilicon IP. Add bindings for them.
+
+Signed-off-by: Shengyang Chen <shengyang.chen@starfivetech.com>
+---
+ .../display/verisilicon/starfive-hdmi.yaml | 92 +++++++++++++++
+ .../display/verisilicon/verisilicon-dc.yaml | 109 ++++++++++++++++++
+ .../display/verisilicon/verisilicon-drm.yaml | 41 +++++++
+ 3 files changed, 242 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/starfive-hdmi.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/verisilicon-drm.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/starfive-hdmi.yaml
+@@ -0,0 +1,92 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/starfive-hdmi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive SoC HDMI transmiter
++
++description:
++ The StarFive SoC uses the HDMI signal transmiter based on innosilicon IP
++ to generate HDMI signal from its input and transmit the signal to the screen.
++
++maintainers:
++ - Keith Zhao <keith.zhao@starfivetech.com>
++
++properties:
++ compatible:
++ const: starfive,hdmi
++
++ reg:
++ minItems: 1
++
++ interrupts:
++ items:
++ - description: The HDMI hot plug detection interrupt.
++
++ clocks:
++ items:
++ - description: System clock of HDMI module.
++ - description: Mclk clock of HDMI audio.
++ - description: Bclk clock of HDMI audio.
++ - description: Pixel clock generated by HDMI module.
++
++ clock-names:
++ items:
++ - const: sysclk
++ - const: mclk
++ - const: bclk
++ - const: pclk
++
++ resets:
++ items:
++ - description: Reset for HDMI module.
++
++ reset-names:
++ items:
++ - const: hdmi_tx
++
++ '#sound-dai-cells':
++ const: 0
++
++ port:
++ $ref: /schemas/graph.yaml#/properties/port
++ description:
++ Port node with one endpoint connected to a display connector node.
++
++required:
++ - compatible
++ - reg
++ - interrupts
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
++ - '#sound-dai-cells'
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ hdmi: hdmi@29590000 {
++ compatible = "starfive,hdmi";
++ reg = <0x29590000 0x4000>;
++ interrupts = <99>;
++ clocks = <&voutcrg 17>,
++ <&voutcrg 15>,
++ <&voutcrg 16>,
++ <&hdmitx0_pixelclk>;
++ clock-names = "sysclk", "mclk","bclk","pclk";
++ resets = <&voutcrg 9>;
++ reset-names = "hdmi_tx";
++ #sound-dai-cells = <0>;
++ hdmi_in: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ hdmi_input: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&dc_out_dpi0>;
++ };
++ };
++ };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
+@@ -0,0 +1,109 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/verisilicon-dc.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive SoC display controller
++
++description:
++ The StarFive SoC uses the display controller based on Verisilicon IP
++ to transfer the image data from a video memory
++ buffer to an external LCD interface.
++
++maintainers:
++ - Keith Zhao <keith.zhao@starfivetech.com>
++
++properties:
++ compatible:
++ const: verisilicon,dc8200
++
++ reg:
++ maxItems: 3
++
++ interrupts:
++ items:
++ - description: The interrupt will be generated when DC finish one frame
++
++ clocks:
++ items:
++ - description: Clock for display system noc bus.
++ - description: Pixel clock for display channel 0.
++ - description: Pixel clock for display channel 1.
++ - description: Clock for axi interface of display controller.
++ - description: Core clock for display controller.
++ - description: Clock for ahb interface of display controller.
++ - description: External HDMI pixel clock.
++ - description: Parent clock for pixel clock
++
++ clock-names:
++ items:
++ - const: clk_vout_noc_disp
++ - const: clk_vout_pix0
++ - const: clk_vout_pix1
++ - const: clk_vout_axi
++ - const: clk_vout_core
++ - const: clk_vout_vout_ahb
++ - const: hdmitx0_pixel
++ - const: clk_vout_dc8200
++
++ resets:
++ items:
++ - description: Reset for axi interface of display controller.
++ - description: Reset for ahb interface of display controller.
++ - description: Core reset of display controller.
++
++ reset-names:
++ items:
++ - const: rst_vout_axi
++ - const: rst_vout_ahb
++ - const: rst_vout_core
++
++ port:
++ $ref: /schemas/graph.yaml#/properties/port
++ description:
++ Port node with one endpoint connected to a hdmi node.
++
++required:
++ - compatible
++ - reg
++ - interrupts
++ - clocks
++ - clock-names
++ - resets
++ - reset-names
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ dc8200: dc8200@29400000 {
++ compatible = "verisilicon,dc8200";
++ reg = <0x29400000 0x100>,
++ <0x29400800 0x2000>,
++ <0x295B0000 0x90>;
++ interrupts = <95>;
++ clocks = <&syscrg 60>,
++ <&voutcrg 7>,
++ <&voutcrg 8>,
++ <&voutcrg 4>,
++ <&voutcrg 5>,
++ <&voutcrg 6>,
++ <&hdmitx0_pixelclk>,
++ <&voutcrg 1>;
++ clock-names = "clk_vout_noc_disp", "clk_vout_pix0", "clk_vout_pix1", "clk_vout_axi",
++ "clk_vout_core", "clk_vout_vout_ahb", "hdmitx0_pixel","clk_vout_dc8200";
++ resets = <&voutcrg 0>,
++ <&voutcrg 1>,
++ <&voutcrg 2>;
++ reset-names = "rst_vout_axi","rst_vout_ahb","rst_vout_core";
++ dc_out: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ dc_out_dpi0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&hdmi_input>;
++ };
++ };
++ };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/verisilicon-drm.yaml
+@@ -0,0 +1,41 @@
++# SPDX-License-Identifier: (GPL-2.0-only)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/verisilicon-drm.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Verisilicon DRM master device
++
++maintainers:
++ - Keith Zhao <keith.zhao@starfivetech.com>
++
++description: |
++ The Verisilicon DRM master device is a virtual device needed to list all
++ display controller or other display interface nodes that comprise the
++ graphics subsystem.
++
++properties:
++ compatible:
++ const: verisilicon,display-subsystem
++
++ ports:
++ $ref: /schemas/types.yaml#/definitions/phandle-array
++ items:
++ maxItems: 1
++ description: |
++ Should contain a list of phandles pointing to display interface ports
++ of display controller devices. Display controller definitions as defined in
++ Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
++
++required:
++ - compatible
++ - ports
++
++additionalProperties: false
++
++examples:
++ - |
++ display-subsystem {
++ compatible = "verisilicon,display-subsystem";
++ ports = <&dc_out>;
++ };
--- /dev/null
+From 5cbf4154da600af8a2e6a2f8d57d1f212abf3f92 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 13 Oct 2023 14:10:24 +0800
+Subject: [PATCH 090/116] soc: starfive: jh71xx_pmu: Add EVENT_TURN_OFF
+ register writing support
+
+Add and export starfive_pmu_hw_event_turn_off_mask() to
+write EVENT_TURN_OFF register.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 11 ++++++++++
+ include/soc/starfive/jh7110_pmu.h | 29 ++++++++++++++++++++++++++
+ 2 files changed, 40 insertions(+)
+ create mode 100644 include/soc/starfive/jh7110_pmu.h
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -16,6 +16,7 @@
+ #include <dt-bindings/power/starfive,jh7110-pmu.h>
+
+ /* register offset */
++#define JH71XX_PMU_HW_EVENT_TURN_OFF 0x08
+ #define JH71XX_PMU_SW_TURN_ON_POWER 0x0C
+ #define JH71XX_PMU_SW_TURN_OFF_POWER 0x10
+ #define JH71XX_PMU_SW_ENCOURAGE 0x44
+@@ -83,6 +84,14 @@ struct jh71xx_pmu_dev {
+ struct generic_pm_domain genpd;
+ };
+
++static void __iomem *pmu_base;
++
++void starfive_pmu_hw_event_turn_off_mask(u32 mask)
++{
++ writel(mask, pmu_base + JH71XX_PMU_HW_EVENT_TURN_OFF);
++}
++EXPORT_SYMBOL(starfive_pmu_hw_event_turn_off_mask);
++
+ static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_on)
+ {
+ struct jh71xx_pmu *pmu = pmd->pmu;
+@@ -334,6 +343,8 @@ static int jh71xx_pmu_probe(struct platf
+ if (IS_ERR(pmu->base))
+ return PTR_ERR(pmu->base);
+
++ pmu_base = pmu->base;
++
+ spin_lock_init(&pmu->lock);
+
+ match_data = of_device_get_match_data(dev);
+--- /dev/null
++++ b/include/soc/starfive/jh7110_pmu.h
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * PMU driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 samin <samin.guo@starfivetech.com>
++ */
++
++#ifndef __SOC_STARFIVE_JH7110_PMU_H__
++#define __SOC_STARFIVE_JH7110_PMU_H__
++
++#include <linux/bits.h>
++#include <linux/types.h>
++
++enum PMU_HARD_EVENT {
++ PMU_HW_EVENT_RTC = BIT(0),
++ PMU_HW_EVENT_GMAC = BIT(1),
++ PMU_HW_EVENT_RFU = BIT(2),
++ PMU_HW_EVENT_RGPIO0 = BIT(3),
++ PMU_HW_EVENT_RGPIO1 = BIT(4),
++ PMU_HW_EVENT_RGPIO2 = BIT(5),
++ PMU_HW_EVENT_RGPIO3 = BIT(6),
++ PMU_HW_EVENT_GPU = BIT(7),
++ PMU_HW_EVENT_ALL = GENMASK(7, 0),
++};
++
++void starfive_pmu_hw_event_turn_off_mask(u32 mask);
++
++#endif /* __SOC_STARFIVE_JH7110_PMU_H__ */
++
--- /dev/null
+From ab436ea7ffb6c464616addad98632c81a321844a Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 12 Dec 2023 17:56:58 +0800
+Subject: [PATCH 091/116] workqueue: Enable flush_scheduled_work()
+
+Enable flush_scheduled_work() for JH7110 GPU.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ include/linux/workqueue.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/include/linux/workqueue.h
++++ b/include/linux/workqueue.h
+@@ -636,7 +636,6 @@ extern void __warn_flushing_systemwide_w
+ /* Please stop using this function, for this function will be removed in near future. */
+ #define flush_scheduled_work() \
+ ({ \
+- __warn_flushing_systemwide_wq(); \
+ __flush_workqueue(system_wq); \
+ })
+
--- /dev/null
+From dedbbfd891e4d0cdb825454eddfbf8386d0025b3 Mon Sep 17 00:00:00 2001
+From: Mason Huo <mason.huo@starfivetech.com>
+Date: Tue, 20 Jun 2023 13:37:52 +0800
+Subject: [PATCH 092/116] riscv: Optimize memcpy with aligned version
+
+Optimizing the 128 byte align case, this will improve the
+performance of large block memcpy.
+
+Here we combine the memcpy of glibc and kernel.
+
+Signed-off-by: Mason Huo <mason.huo@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/lib/Makefile | 3 +-
+ arch/riscv/lib/{memcpy.S => memcpy_aligned.S} | 36 +--
+ arch/riscv/lib/string.c | 266 ++++++++++++++++++
+ 3 files changed, 273 insertions(+), 32 deletions(-)
+ rename arch/riscv/lib/{memcpy.S => memcpy_aligned.S} (67%)
+ create mode 100644 arch/riscv/lib/string.c
+
+--- a/arch/riscv/lib/Makefile
++++ b/arch/riscv/lib/Makefile
+@@ -1,6 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ lib-y += delay.o
+-lib-y += memcpy.o
+ lib-y += memset.o
+ lib-y += memmove.o
+ lib-y += strcmp.o
+@@ -9,5 +8,7 @@ lib-y += strncmp.o
+ lib-$(CONFIG_MMU) += uaccess.o
+ lib-$(CONFIG_64BIT) += tishift.o
+ lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o
++lib-y += string.o
++lib-y += memcpy_aligned.o
+
+ obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
+--- a/arch/riscv/lib/memcpy.S
++++ /dev/null
+@@ -1,110 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- * Copyright (C) 2013 Regents of the University of California
+- */
+-
+-#include <linux/linkage.h>
+-#include <asm/asm.h>
+-
+-/* void *memcpy(void *, const void *, size_t) */
+-ENTRY(__memcpy)
+-WEAK(memcpy)
+- move t6, a0 /* Preserve return value */
+-
+- /* Defer to byte-oriented copy for small sizes */
+- sltiu a3, a2, 128
+- bnez a3, 4f
+- /* Use word-oriented copy only if low-order bits match */
+- andi a3, t6, SZREG-1
+- andi a4, a1, SZREG-1
+- bne a3, a4, 4f
+-
+- beqz a3, 2f /* Skip if already aligned */
+- /*
+- * Round to nearest double word-aligned address
+- * greater than or equal to start address
+- */
+- andi a3, a1, ~(SZREG-1)
+- addi a3, a3, SZREG
+- /* Handle initial misalignment */
+- sub a4, a3, a1
+-1:
+- lb a5, 0(a1)
+- addi a1, a1, 1
+- sb a5, 0(t6)
+- addi t6, t6, 1
+- bltu a1, a3, 1b
+- sub a2, a2, a4 /* Update count */
+-
+-2:
+- andi a4, a2, ~((16*SZREG)-1)
+- beqz a4, 4f
+- add a3, a1, a4
+-3:
+- REG_L a4, 0(a1)
+- REG_L a5, SZREG(a1)
+- REG_L a6, 2*SZREG(a1)
+- REG_L a7, 3*SZREG(a1)
+- REG_L t0, 4*SZREG(a1)
+- REG_L t1, 5*SZREG(a1)
+- REG_L t2, 6*SZREG(a1)
+- REG_L t3, 7*SZREG(a1)
+- REG_L t4, 8*SZREG(a1)
+- REG_L t5, 9*SZREG(a1)
+- REG_S a4, 0(t6)
+- REG_S a5, SZREG(t6)
+- REG_S a6, 2*SZREG(t6)
+- REG_S a7, 3*SZREG(t6)
+- REG_S t0, 4*SZREG(t6)
+- REG_S t1, 5*SZREG(t6)
+- REG_S t2, 6*SZREG(t6)
+- REG_S t3, 7*SZREG(t6)
+- REG_S t4, 8*SZREG(t6)
+- REG_S t5, 9*SZREG(t6)
+- REG_L a4, 10*SZREG(a1)
+- REG_L a5, 11*SZREG(a1)
+- REG_L a6, 12*SZREG(a1)
+- REG_L a7, 13*SZREG(a1)
+- REG_L t0, 14*SZREG(a1)
+- REG_L t1, 15*SZREG(a1)
+- addi a1, a1, 16*SZREG
+- REG_S a4, 10*SZREG(t6)
+- REG_S a5, 11*SZREG(t6)
+- REG_S a6, 12*SZREG(t6)
+- REG_S a7, 13*SZREG(t6)
+- REG_S t0, 14*SZREG(t6)
+- REG_S t1, 15*SZREG(t6)
+- addi t6, t6, 16*SZREG
+- bltu a1, a3, 3b
+- andi a2, a2, (16*SZREG)-1 /* Update count */
+-
+-4:
+- /* Handle trailing misalignment */
+- beqz a2, 6f
+- add a3, a1, a2
+-
+- /* Use word-oriented copy if co-aligned to word boundary */
+- or a5, a1, t6
+- or a5, a5, a3
+- andi a5, a5, 3
+- bnez a5, 5f
+-7:
+- lw a4, 0(a1)
+- addi a1, a1, 4
+- sw a4, 0(t6)
+- addi t6, t6, 4
+- bltu a1, a3, 7b
+-
+- ret
+-
+-5:
+- lb a4, 0(a1)
+- addi a1, a1, 1
+- sb a4, 0(t6)
+- addi t6, t6, 1
+- bltu a1, a3, 5b
+-6:
+- ret
+-END(__memcpy)
+-SYM_FUNC_ALIAS(__pi_memcpy, __memcpy)
+-SYM_FUNC_ALIAS(__pi___memcpy, __memcpy)
+--- /dev/null
++++ b/arch/riscv/lib/memcpy_aligned.S
+@@ -0,0 +1,84 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2013 Regents of the University of California
++ */
++
++#include <linux/linkage.h>
++#include <asm/asm.h>
++
++/* void *__memcpy_aligned(void *, const void *, size_t) */
++ENTRY(__memcpy_aligned)
++ move t6, a0 /* Preserve return value */
++
++2:
++ andi a4, a2, ~((16*SZREG)-1)
++ beqz a4, 4f
++ add a3, a1, a4
++3:
++ REG_L a4, 0(a1)
++ REG_L a5, SZREG(a1)
++ REG_L a6, 2*SZREG(a1)
++ REG_L a7, 3*SZREG(a1)
++ REG_L t0, 4*SZREG(a1)
++ REG_L t1, 5*SZREG(a1)
++ REG_L t2, 6*SZREG(a1)
++ REG_L t3, 7*SZREG(a1)
++ REG_L t4, 8*SZREG(a1)
++ REG_L t5, 9*SZREG(a1)
++ REG_S a4, 0(t6)
++ REG_S a5, SZREG(t6)
++ REG_S a6, 2*SZREG(t6)
++ REG_S a7, 3*SZREG(t6)
++ REG_S t0, 4*SZREG(t6)
++ REG_S t1, 5*SZREG(t6)
++ REG_S t2, 6*SZREG(t6)
++ REG_S t3, 7*SZREG(t6)
++ REG_S t4, 8*SZREG(t6)
++ REG_S t5, 9*SZREG(t6)
++ REG_L a4, 10*SZREG(a1)
++ REG_L a5, 11*SZREG(a1)
++ REG_L a6, 12*SZREG(a1)
++ REG_L a7, 13*SZREG(a1)
++ REG_L t0, 14*SZREG(a1)
++ REG_L t1, 15*SZREG(a1)
++ addi a1, a1, 16*SZREG
++ REG_S a4, 10*SZREG(t6)
++ REG_S a5, 11*SZREG(t6)
++ REG_S a6, 12*SZREG(t6)
++ REG_S a7, 13*SZREG(t6)
++ REG_S t0, 14*SZREG(t6)
++ REG_S t1, 15*SZREG(t6)
++ addi t6, t6, 16*SZREG
++ bltu a1, a3, 3b
++ andi a2, a2, (16*SZREG)-1 /* Update count */
++
++4:
++ /* Handle trailing misalignment */
++ beqz a2, 6f
++ add a3, a1, a2
++
++ /* Use word-oriented copy if co-aligned to word boundary */
++ or a5, a1, t6
++ or a5, a5, a3
++ andi a5, a5, 3
++ bnez a5, 5f
++7:
++ lw a4, 0(a1)
++ addi a1, a1, 4
++ sw a4, 0(t6)
++ addi t6, t6, 4
++ bltu a1, a3, 7b
++
++ ret
++
++5:
++ lb a4, 0(a1)
++ addi a1, a1, 1
++ sb a4, 0(t6)
++ addi t6, t6, 1
++ bltu a1, a3, 5b
++6:
++ ret
++END(__memcpy_aligned)
++SYM_FUNC_ALIAS(__pi_memcpy, __memcpy_aligned)
++SYM_FUNC_ALIAS(__pi___memcpy, __memcpy_aligned)
+--- /dev/null
++++ b/arch/riscv/lib/string.c
+@@ -0,0 +1,266 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copy memory to memory until the specified number of bytes
++ * has been copied. Overlap is NOT handled correctly.
++ * Copyright (C) 1991-2020 Free Software Foundation, Inc.
++ * This file is part of the GNU C Library.
++ * Contributed by Torbjorn Granlund (tege@sics.se).
++ *
++ * The GNU C Library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * The GNU C Library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with the GNU C Library; if not, see
++ * <https://www.gnu.org/licenses/>.
++ *
++ */
++
++#define __NO_FORTIFY
++#include <linux/types.h>
++#include <linux/module.h>
++
++#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
++#define OP_T_THRES 16
++#define op_t unsigned long
++#define OPSIZ (sizeof(op_t))
++#define OPSIZ_MASK (sizeof(op_t) - 1)
++#define FAST_COPY_THRES (128)
++#define byte unsigned char
++
++static void _wordcopy_fwd_aligned(long dstp, long srcp, size_t len)
++{
++ op_t a0, a1;
++
++ switch (len % 8) {
++ case 2:
++ a0 = ((op_t *) srcp)[0];
++ srcp -= 6 * OPSIZ;
++ dstp -= 7 * OPSIZ;
++ len += 6;
++ goto do1;
++ case 3:
++ a1 = ((op_t *) srcp)[0];
++ srcp -= 5 * OPSIZ;
++ dstp -= 6 * OPSIZ;
++ len += 5;
++ goto do2;
++ case 4:
++ a0 = ((op_t *) srcp)[0];
++ srcp -= 4 * OPSIZ;
++ dstp -= 5 * OPSIZ;
++ len += 4;
++ goto do3;
++ case 5:
++ a1 = ((op_t *) srcp)[0];
++ srcp -= 3 * OPSIZ;
++ dstp -= 4 * OPSIZ;
++ len += 3;
++ goto do4;
++ case 6:
++ a0 = ((op_t *) srcp)[0];
++ srcp -= 2 * OPSIZ;
++ dstp -= 3 * OPSIZ;
++ len += 2;
++ goto do5;
++ case 7:
++ a1 = ((op_t *) srcp)[0];
++ srcp -= 1 * OPSIZ;
++ dstp -= 2 * OPSIZ;
++ len += 1;
++ goto do6;
++
++ case 0:
++ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++ return;
++ a0 = ((op_t *) srcp)[0];
++ srcp -= 0 * OPSIZ;
++ dstp -= 1 * OPSIZ;
++ goto do7;
++ case 1:
++ a1 = ((op_t *) srcp)[0];
++ srcp -= -1 * OPSIZ;
++ dstp -= 0 * OPSIZ;
++ len -= 1;
++ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++ goto do0;
++ goto do8; /* No-op. */
++ }
++
++ do {
++do8:
++ a0 = ((op_t *) srcp)[0];
++ ((op_t *) dstp)[0] = a1;
++do7:
++ a1 = ((op_t *) srcp)[1];
++ ((op_t *) dstp)[1] = a0;
++do6:
++ a0 = ((op_t *) srcp)[2];
++ ((op_t *) dstp)[2] = a1;
++do5:
++ a1 = ((op_t *) srcp)[3];
++ ((op_t *) dstp)[3] = a0;
++do4:
++ a0 = ((op_t *) srcp)[4];
++ ((op_t *) dstp)[4] = a1;
++do3:
++ a1 = ((op_t *) srcp)[5];
++ ((op_t *) dstp)[5] = a0;
++do2:
++ a0 = ((op_t *) srcp)[6];
++ ((op_t *) dstp)[6] = a1;
++do1:
++ a1 = ((op_t *) srcp)[7];
++ ((op_t *) dstp)[7] = a0;
++
++ srcp += 8 * OPSIZ;
++ dstp += 8 * OPSIZ;
++ len -= 8;
++ } while (len != 0);
++
++ /* This is the right position for do0. Please don't move
++ * it into the loop.
++ */
++do0:
++ ((op_t *) dstp)[0] = a1;
++}
++
++static void _wordcopy_fwd_dest_aligned(long dstp, long srcp, size_t len)
++{
++ op_t a0, a1, a2, a3;
++ int sh_1, sh_2;
++
++ /* Calculate how to shift a word read at the memory operation
++ * aligned srcp to make it aligned for copy.
++ */
++
++ sh_1 = 8 * (srcp % OPSIZ);
++ sh_2 = 8 * OPSIZ - sh_1;
++
++ /* Make SRCP aligned by rounding it down to the beginning of the `op_t'
++ * it points in the middle of.
++ */
++ srcp &= -OPSIZ;
++
++ switch (len % 4) {
++ case 2:
++ a1 = ((op_t *) srcp)[0];
++ a2 = ((op_t *) srcp)[1];
++ srcp -= 1 * OPSIZ;
++ dstp -= 3 * OPSIZ;
++ len += 2;
++ goto do1;
++ case 3:
++ a0 = ((op_t *) srcp)[0];
++ a1 = ((op_t *) srcp)[1];
++ srcp -= 0 * OPSIZ;
++ dstp -= 2 * OPSIZ;
++ len += 1;
++ goto do2;
++ case 0:
++ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++ return;
++ a3 = ((op_t *) srcp)[0];
++ a0 = ((op_t *) srcp)[1];
++ srcp -= -1 * OPSIZ;
++ dstp -= 1 * OPSIZ;
++ len += 0;
++ goto do3;
++ case 1:
++ a2 = ((op_t *) srcp)[0];
++ a3 = ((op_t *) srcp)[1];
++ srcp -= -2 * OPSIZ;
++ dstp -= 0 * OPSIZ;
++ len -= 1;
++ if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++ goto do0;
++ goto do4; /* No-op. */
++ }
++
++ do {
++do4:
++ a0 = ((op_t *) srcp)[0];
++ ((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
++do3:
++ a1 = ((op_t *) srcp)[1];
++ ((op_t *) dstp)[1] = MERGE(a3, sh_1, a0, sh_2);
++do2:
++ a2 = ((op_t *) srcp)[2];
++ ((op_t *) dstp)[2] = MERGE(a0, sh_1, a1, sh_2);
++do1:
++ a3 = ((op_t *) srcp)[3];
++ ((op_t *) dstp)[3] = MERGE(a1, sh_1, a2, sh_2);
++
++ srcp += 4 * OPSIZ;
++ dstp += 4 * OPSIZ;
++ len -= 4;
++ } while (len != 0);
++
++ /* This is the right position for do0. Please don't move
++ * it into the loop.
++ */
++do0:
++ ((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
++}
++
++#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \
++do { \
++ size_t __nbytes = (nbytes); \
++ while (__nbytes > 0) { \
++ byte __x = ((byte *) src_bp)[0]; \
++ src_bp += 1; \
++ __nbytes -= 1; \
++ ((byte *) dst_bp)[0] = __x; \
++ dst_bp += 1; \
++ } \
++} while (0)
++
++#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \
++do { \
++ if (src_bp % OPSIZ == 0) \
++ _wordcopy_fwd_aligned(dst_bp, src_bp, (nbytes) / OPSIZ); \
++ else \
++ _wordcopy_fwd_dest_aligned(dst_bp, src_bp, (nbytes) / OPSIZ); \
++ src_bp += (nbytes) & -OPSIZ; \
++ dst_bp += (nbytes) & -OPSIZ; \
++ (nbytes_left) = (nbytes) % OPSIZ; \
++} while (0)
++
++extern void *__memcpy_aligned(void *dest, const void *src, size_t len);
++void *__memcpy(void *dest, const void *src, size_t len)
++{
++ unsigned long dstp = (long) dest;
++ unsigned long srcp = (long) src;
++
++ /* If there not too few bytes to copy, use word copy. */
++ if (len >= OP_T_THRES) {
++ if ((len >= FAST_COPY_THRES) && ((dstp & OPSIZ_MASK) == 0) &&
++ ((srcp & OPSIZ_MASK) == 0)) {
++ __memcpy_aligned(dest, src, len);
++ return dest;
++ }
++ /* Copy just a few bytes to make DSTP aligned. */
++ len -= (-dstp) % OPSIZ;
++ BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
++
++ /* Copy from SRCP to DSTP taking advantage of the known alignment of
++ * DSTP. Number of bytes remaining is put in the third argument,
++ * i.e. in LEN. This number may vary from machine to machine.
++ */
++ WORD_COPY_FWD(dstp, srcp, len, len);
++ /* Fall out and copy the tail. */
++ }
++
++ /* There are just a few bytes to copy. Use byte memory operations. */
++ BYTE_COPY_FWD(dstp, srcp, len);
++
++ return dest;
++}
++
++void *memcpy(void *dest, const void *src, size_t len) __weak __alias(__memcpy);
--- /dev/null
+From 41c9e97bb70321f7848bd489e45246a9dc985974 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Sun, 4 Feb 2024 15:27:09 +0800
+Subject: [PATCH 093/116] riscv/purgatory: Change memcpy to the aligned version
+
+Change memcpy to the aligned version, for purgatory.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/purgatory/Makefile | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/purgatory/Makefile
++++ b/arch/riscv/purgatory/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ OBJECT_FILES_NON_STANDARD := y
+
+-purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
++purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy_aligned.o memcpy.o memset.o
+ purgatory-y += strcmp.o strlen.o strncmp.o
+
+ targets += $(purgatory-y)
+@@ -13,9 +13,12 @@ $(obj)/string.o: $(srctree)/lib/string.c
+ $(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
+ $(call if_changed_rule,cc_o_c)
+
+-$(obj)/memcpy.o: $(srctree)/arch/riscv/lib/memcpy.S FORCE
++$(obj)/memcpy_aligned.o: $(srctree)/arch/riscv/lib/memcpy_aligned.S FORCE
+ $(call if_changed_rule,as_o_S)
+
++$(obj)/memcpy.o: $(srctree)/arch/riscv/lib/string.c FORCE
++ $(call if_changed_rule,cc_o_c)
++
+ $(obj)/memset.o: $(srctree)/arch/riscv/lib/memset.S FORCE
+ $(call if_changed_rule,as_o_S)
+
--- /dev/null
+From f36d458104101050478e290919ef4f05fbde0b3e Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 3 Jul 2023 16:52:13 +0800
+Subject: [PATCH 094/116] Add 16 ISP controls, remove the frame SYNC event to
+ video7 (SC) These controls are: WB, CAR, CCM, CFA, CTC, DBC, DNYUV, GMARGB,
+ LCCF, OBC, OECF, R2Y, SAT, SHRP, YCRV, SC
+
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c | 628 ++++++++++++++++++
+ .../platform/starfive/v4l2_driver/stf_video.c | 22 +
+ .../platform/starfive/v4l2_driver/stf_vin.c | 16 +-
+ include/uapi/linux/jh7110-isp.h | 48 +-
+ 4 files changed, 706 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -195,6 +195,41 @@ static const char * const test_pattern_m
+ "Color squares w/ rolling bar",
+ };
+
++enum isp_modules_index {
++ imi_obc = 0,
++ imi_oecf,
++ imi_lccf,
++ imi_awb,
++ imi_dbc,
++ imi_ctc,
++ imi_cfa,
++ imi_car,
++ imi_ccm,
++ imi_gmargb,
++ imi_r2y,
++ imi_ycrv,
++ imi_shrp,
++ imi_dnyuv,
++ imi_sat,
++ imi_sc
++};
++
++#define MODULE_ENABLE_REGISTER0 0x10
++#define MODULE_ENABLE_REGISTER1 0xa08
++
++struct module_register_info {
++ __u32 en_reg;
++ __u32 en_nbit;
++ __u32 cfg_reg;
++};
++
++static const struct module_register_info mod_reg_info[] = {
++ {MODULE_ENABLE_REGISTER0, 2, 0x034}, {MODULE_ENABLE_REGISTER0, 4, 0x100}, {MODULE_ENABLE_REGISTER0, 6, 0x050}, {MODULE_ENABLE_REGISTER0, 7, 0x280},
++ {MODULE_ENABLE_REGISTER1, 22, 0xa14}, {MODULE_ENABLE_REGISTER1, 21, 0xa10}, {MODULE_ENABLE_REGISTER1, 1, 0xa1c}, {MODULE_ENABLE_REGISTER1, 2, 0x000},
++ {MODULE_ENABLE_REGISTER1, 3, 0xc40}, {MODULE_ENABLE_REGISTER1, 4, 0xe00}, {MODULE_ENABLE_REGISTER1, 5, 0xe40}, {MODULE_ENABLE_REGISTER1, 19, 0xf00},
++ {MODULE_ENABLE_REGISTER1, 7, 0xe80}, {MODULE_ENABLE_REGISTER1, 17, 0xc00}, {MODULE_ENABLE_REGISTER1, 8, 0xa30}, {MODULE_ENABLE_REGISTER0, 17, 0x09c}
++};
++
+ #define ISP_TEST_ENABLE BIT(7)
+ #define ISP_TEST_ROLLING BIT(6) /* rolling horizontal bar */
+ #define ISP_TEST_TRANSPARENT BIT(5)
+@@ -260,6 +295,401 @@ static int isp_g_volatile_ctrl(struct v4
+ return 0;
+ }
+
++#define CREATE_REG_VALUE_FUNCTION(type) \
++ static u32 create_reg_value_##type(const type * value, s32 size, u32 mask, s32 nbits) { \
++ s32 npos = 0; \
++ u32 res = 0; \
++ s32 sz = size; \
++ s32 i = 0; \
++ if(size * nbits > 32) sz = 32 / nbits; \
++ for(i = 0; i < sz; i++, npos += nbits, value++) res |= (u32)(value[0] & mask) << npos; \
++ return res; \
++}
++
++CREATE_REG_VALUE_FUNCTION(u8);
++CREATE_REG_VALUE_FUNCTION(u16);
++#define CREATE_REG_VALUE(type, value, size, mask, nbits) create_reg_value_##type(value, size, mask, nbits)
++
++#define FILL_ISP_REGS_FUNC(type) \
++static void fill_isp_regs_##type(void __iomem *ispbase, u32 offset, const type * value, s32 size, u32 mask, u32 nbits) { \
++ s32 i; \
++ for(i = 0; i < size; i++, value++) \
++ reg_write(ispbase, offset + i * 4, (u32)(value[0] & mask) << nbits); \
++}
++FILL_ISP_REGS_FUNC(u32);
++FILL_ISP_REGS_FUNC(u8);
++FILL_ISP_REGS_FUNC(u16);
++
++#define FILL_ISP_REGS(type, ispbase, offset, value, size, mask, nbits) \
++ fill_isp_regs_##type(ispbase, offset, value, size, mask, nbits)
++
++static int isp_set_ctrl_wb(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_awb];
++ const struct jh7110_isp_wb_setting * setting = (const struct jh7110_isp_wb_setting *)value;
++ const struct jh7110_isp_wb_gain * gains = &setting->gains;
++ u32 r_g = (((u32)gains->gain_r << 16) | gains->gain_r) & 0x03ff03ff;
++ u32 g_g = (((u32)gains->gain_g << 16) | gains->gain_g) & 0x03ff03ff;
++ u32 b_g = (((u32)gains->gain_b << 16) | gains->gain_b) & 0x03ff03ff;
++ u32 reg_addr = reg_info->cfg_reg + 16 * sizeof(u32);
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_write(ispbase, reg_addr, r_g);
++ reg_write(ispbase, reg_addr + 1 * 4, r_g);
++ reg_write(ispbase, reg_addr + 2 * 4, g_g);
++ reg_write(ispbase, reg_addr + 3 * 4, g_g);
++ reg_write(ispbase, reg_addr + 4 * 4, g_g);
++ reg_write(ispbase, reg_addr + 5 * 4, g_g);
++ reg_write(ispbase, reg_addr + 6 * 4, b_g);
++ reg_write(ispbase, reg_addr + 7 * 4, b_g);
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_car(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_car];
++ const struct jh7110_isp_car_setting * setting = (const struct jh7110_isp_car_setting *)value;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_ccm(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_ccm];
++ const struct jh7110_isp_ccm_setting * setting = (const struct jh7110_isp_ccm_setting *)value;
++ const struct jh7110_isp_ccm_smlow * ccm = (const struct jh7110_isp_ccm_smlow *)(&(setting->ccm_smlow));
++ u32 reg_addr = reg_info->cfg_reg + 12 * sizeof(u32);
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_write(ispbase, reg_info->cfg_reg, 6 << 16);
++ FILL_ISP_REGS(u32, ispbase, reg_addr, (u32 *)ccm, 12, 0x7ff, 0);
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_cfa(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_cfa];
++ const struct jh7110_isp_cfa_setting * setting = (const struct jh7110_isp_cfa_setting *)value;
++ const struct jh7110_isp_cfa_params * cfg = (const struct jh7110_isp_cfa_params *)(&(setting->settings));
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_write(ispbase, reg_addr, ((u32)(cfg->cross_cov & 0x3) << 4) | (cfg->hv_width & 0xf));
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_ctc(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_ctc];
++ const struct jh7110_isp_ctc_setting * setting = (const struct jh7110_isp_ctc_setting *)value;
++ const struct jh7110_isp_ctc_params * cfg = (const struct jh7110_isp_ctc_params *)(&(setting->settings));
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ u32 reg_value = (u32)(((cfg->saf_mode & 1) << 1) | (cfg->daf_mode & 1)) << 30;
++
++ reg_value |= ((u32)(cfg->max_gt & 0x3ff) << 16) | (cfg->min_gt & 0x3ff);
++ reg_write(ispbase, reg_addr, reg_value);
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_dbc(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_dbc];
++ const struct jh7110_isp_dbc_setting * setting = (const struct jh7110_isp_dbc_setting *)value;
++ const struct jh7110_isp_dbc_params * cfg = (const struct jh7110_isp_dbc_params *)(&(setting->settings));
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_write(ispbase, reg_addr, ((u32)(cfg->bad_gt & 0x3ff) << 16) | (cfg->bad_xt & 0x3ff));
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_dnyuv(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_dnyuv];
++ const struct jh7110_isp_dnyuv_setting * setting = (const struct jh7110_isp_dnyuv_setting *)value;
++ const struct jh7110_isp_dnyuv_params * cfg = (const struct jh7110_isp_dnyuv_params *)(&(setting->settings));
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, cfg->y_sweight, 6, 0x7, 4));
++ reg_write(ispbase, reg_addr + 4, CREATE_REG_VALUE(u8, &cfg->y_sweight[6], 4, 0x7, 4));
++ reg_write(ispbase, reg_addr + 8, CREATE_REG_VALUE(u8, cfg->uv_sweight, 6, 0x7, 4));
++ reg_write(ispbase, reg_addr + 12, CREATE_REG_VALUE(u8, &cfg->uv_sweight[6], 4, 0x7, 4));
++ reg_write(ispbase, reg_addr + 16, CREATE_REG_VALUE(u16, &cfg->y_curve[0], 2, 0x3ff, 16));
++ reg_write(ispbase, reg_addr + 20, CREATE_REG_VALUE(u16, &cfg->y_curve[2], 2, 0x3ff, 16));
++ reg_write(ispbase, reg_addr + 24, CREATE_REG_VALUE(u16, &cfg->y_curve[4], 2, 0x3ff, 16));
++ reg_write(ispbase, reg_addr + 28, CREATE_REG_VALUE(u16, &cfg->uv_curve[0], 2, 0x3ff, 16));
++ reg_write(ispbase, reg_addr + 32, CREATE_REG_VALUE(u16, &cfg->uv_curve[2], 2, 0x3ff, 16));
++ reg_write(ispbase, reg_addr + 36, CREATE_REG_VALUE(u16, &cfg->uv_curve[4], 2, 0x3ff, 16));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_gmargb(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_gmargb];
++ const struct jh7110_isp_gmargb_setting * setting = (const struct jh7110_isp_gmargb_setting *)value;
++ const struct jh7110_isp_gmargb_point * curve = setting->curve;
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ for(i = 0; i < 15; i++, curve++, reg_addr += 4)
++ reg_write(ispbase, reg_addr, ((u32)curve->sg_val << 16) | (curve->g_val & 0x3ff));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_lccf(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_lccf];
++ const struct jh7110_isp_lccf_setting * setting = (const struct jh7110_isp_lccf_setting *)value;
++ const s16 * params = (s16 *)(&setting->r_param);
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ reg_write(ispbase, reg_addr, ((u32)(setting->circle.center_y & 0x7fff) << 16) | (setting->circle.center_x & 0x7fff));
++ reg_write(ispbase, reg_addr + 8, setting->circle.radius & 0xf);
++ reg_addr += 0x90;
++ for(i = 0; i < 4; i++, reg_addr += 4, params += 2)
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x1fff, 16));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_obc(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_obc];
++ const struct jh7110_isp_blacklevel_setting * setting = (const struct jh7110_isp_blacklevel_setting *)value;
++ const u8 * params = (u8 *)setting->gain;
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ reg_write(ispbase, reg_addr, ((u32)(setting->win_size.height & 0xf) << 4) | (setting->win_size.width & 0xf));
++
++ reg_addr += 0x2ac; //0x2e0
++ for(i = 0; i < 8; i++, reg_addr += 4, params += 4)
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 4, 0xff, 8));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_oecf(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_oecf];
++ const struct jh7110_isp_oecf_setting * setting = (const struct jh7110_isp_oecf_setting *)value;
++ const struct jh7110_isp_oecf_point * pts = (struct jh7110_isp_oecf_point *)(setting->r_curve);
++ u32 reg_x_addr = reg_info->cfg_reg;
++ u32 reg_y_addr = reg_info->cfg_reg + 0x080;
++ u32 reg_s_addr = reg_info->cfg_reg + 0x100;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ u32 x, y, slope;
++ s32 i;
++
++ for(i = 0; i < 32; i++, reg_x_addr += 4, reg_y_addr += 4, reg_s_addr += 4) {
++ x = pts->x & 0x3ff;
++ y = pts->y & 0x3ff;
++ slope = pts->slope & 0x3ff;
++ pts++;
++ x |= ((pts->x & 0x3ff) << 16);
++ y |= ((pts->y & 0x3ff) << 16);
++ slope |= ((pts->slope & 0x3ff) << 16);
++ pts++;
++
++ reg_write(ispbase, reg_x_addr, x);
++ reg_write(ispbase, reg_y_addr, y);
++ reg_write(ispbase, reg_s_addr, slope);
++ }
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_r2y(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_r2y];
++ const struct jh7110_isp_r2y_setting * setting = (const struct jh7110_isp_r2y_setting *)value;
++ const s16 * params = setting->matrix.m;
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ for(i = 0; i < 9; i++, reg_addr += 4)
++ reg_write(ispbase, reg_addr, (u32)(params[i] & 0x1ff));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++
++static int isp_set_ctrl_sat(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_sat];
++ const struct jh7110_isp_sat_setting * setting = (const struct jh7110_isp_sat_setting *)value;
++ const u16 * params = (u16 *)(&setting->sat_info);
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ for(i = 0; i < 3; i++, reg_addr += 4, params += 2)
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0xfff, 16));
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, &setting->hue_info.cos, 2, 0x3ff, 16));
++ reg_addr += 4;
++ reg_write(ispbase, reg_addr, setting->sat_info.cmsf & 0xf);
++
++ params = (u16 *)(&setting->curve);
++ reg_addr += 0x14; // 0xa54
++ for(i = 0; i < 2; i++, reg_addr += 4, params += 2)
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x3fff, 16));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_shrp(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_shrp];
++ const struct jh7110_isp_sharp_setting * setting = (const struct jh7110_isp_sharp_setting *)value;
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ s32 i;
++
++ for(i = 0; i < 4; i++, reg_addr += 4)
++ reg_write(ispbase, reg_addr, ((u32)(setting->strength.diff[i] & 0x3ff) << 16) | ((u32)(setting->weight.weight[i] & 0xf) << 8));
++ FILL_ISP_REGS(u8, ispbase, reg_addr, (u8 *)(&setting->weight.weight[4]), 15 - 4, 0xf, 8);
++ reg_addr += (15 - 4) * 4;
++
++ for(i = 0; i < 3; i++, reg_addr += 4)
++ reg_write(ispbase, reg_addr, ((u32)(setting->strength.f[i] & 0x7f) << 24) | (setting->strength.s[i] & 0x1fffff));
++
++ reg_addr += 3 * 4;
++ reg_write(ispbase, reg_addr, ((u32)(setting->pdirf & 0xf) << 28) | ((u32)(setting->ndirf & 0xf) << 24) | (setting->weight.recip_wei_sum & 0x3fffff));
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_ycrv(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_ycrv];
++ const struct jh7110_isp_ycrv_setting * setting = (const struct jh7110_isp_ycrv_setting *)value;
++ u32 reg_addr = reg_info->cfg_reg;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++
++ FILL_ISP_REGS(u16, ispbase, reg_addr, (u16 *)(setting->curve.y), 64, 0x3ff, 0);
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
++static int isp_set_ctrl_sc(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct module_register_info * reg_info = &mod_reg_info[imi_sc];
++ const struct jh7110_isp_sc_setting * setting = (const struct jh7110_isp_sc_setting *)value;
++ const u8 * params = setting->awb_config.awb_cw;
++ u32 reg_addr = 0x00;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ u32 weight_cfg[6] = {0};
++ u32 * weight = weight_cfg;
++ u32 * w_diff = weight_cfg + 2;
++ s32 i;
++
++ // AF register
++ reg_write(ispbase, 0xc0,
++ ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
++ ((u32)(setting->af_config.es_ver_thr & 0xff) << 8) |
++ ((setting->af_config.ver_en & 0x1) << 3) |
++ ((setting->af_config.hor_en & 0x1) << 2) |
++ ((setting->af_config.es_sum_mode & 0x1) << 1) |
++ (setting->af_config.es_hor_mode & 0x1));
++
++ // AWB weight sum register
++ reg_write(ispbase, 0x5d0, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_rl, 4, 0xff, 8));
++ reg_write(ispbase, 0x5d4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_gbl, 4, 0xff, 8));
++
++ // AWB weight value point
++ reg_addr = 0x4d0;
++ for(i = 0; i < 13; i++) {
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 8, 0xf, 4));
++ reg_addr += 4;
++ params += 8;
++ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 5, 0xf, 4));
++ reg_addr += 4;
++ params += 5;
++ }
++
++ // AWB intensity weight curve register
++ reg_addr = 0x538;
++ for(i = 0; i < 4; i++) {
++ weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++ w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++ }
++ for(w_diff++; i < 8; i++) {
++ weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++ w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++ }
++ for(weight++, w_diff++; i < 12; i++) {
++ weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++ w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++ }
++ for(w_diff++; i < 16; i++) {
++ weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++ w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++ }
++
++ FILL_ISP_REGS(u32, ispbase, reg_addr, weight_cfg, 6, 0xffffffff, 0);
++
++ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++ return 0;
++}
++
+ static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+@@ -310,10 +740,52 @@ static int isp_s_ctrl(struct v4l2_ctrl *
+ ret = isp_set_ctrl_vflip(isp_dev, ctrl->val);
+ break;
+ case V4L2_CID_USER_JH7110_ISP_WB_SETTING:
++ ret = isp_set_ctrl_wb(isp_dev, ctrl->p_new.p_u8);
+ break;
+ case V4L2_CID_USER_JH7110_ISP_CAR_SETTING:
++ ret = isp_set_ctrl_car(isp_dev, ctrl->p_new.p_u8);
+ break;
+ case V4L2_CID_USER_JH7110_ISP_CCM_SETTING:
++ ret = isp_set_ctrl_ccm(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_CFA_SETTING:
++ ret = isp_set_ctrl_cfa(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_CTC_SETTING:
++ ret = isp_set_ctrl_ctc(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_DBC_SETTING:
++ ret = isp_set_ctrl_dbc(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING:
++ ret = isp_set_ctrl_dnyuv(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING:
++ ret = isp_set_ctrl_gmargb(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_LCCF_SETTING:
++ ret = isp_set_ctrl_lccf(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_OBC_SETTING:
++ ret = isp_set_ctrl_obc(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_OECF_SETTING:
++ ret = isp_set_ctrl_oecf(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_R2Y_SETTING:
++ ret = isp_set_ctrl_r2y(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_SAT_SETTING:
++ ret = isp_set_ctrl_sat(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_SHRP_SETTING:
++ ret = isp_set_ctrl_shrp(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_YCRV_SETTING:
++ ret = isp_set_ctrl_ycrv(isp_dev, ctrl->p_new.p_u8);
++ break;
++ case V4L2_CID_USER_JH7110_ISP_STAT_SETTING:
++ ret = isp_set_ctrl_sc(isp_dev, ctrl->p_new.p_u8);
+ break;
+ default:
+ ret = -EINVAL;
+@@ -365,6 +837,162 @@ struct v4l2_ctrl_config isp_ctrl[] = {
+ .dims[0] = sizeof(struct jh7110_isp_ccm_setting),
+ .flags = 0,
+ },
++ [3] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "CFA Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_CFA_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_cfa_setting),
++ .flags = 0,
++ },
++ [4] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "CTC Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_CTC_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_ctc_setting),
++ .flags = 0,
++ },
++ [5] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "CTC Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_DBC_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_dbc_setting),
++ .flags = 0,
++ },
++ [6] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "DNYUV Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_dnyuv_setting),
++ .flags = 0,
++ },
++ [7] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "DNYUV Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_gmargb_setting),
++ .flags = 0,
++ },
++ [8] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "LCCF Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_LCCF_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_lccf_setting),
++ .flags = 0,
++ },
++ [9] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "OBC Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_OBC_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_blacklevel_setting),
++ .flags = 0,
++ },
++ [10] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "OECF Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_OECF_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_oecf_setting),
++ .flags = 0,
++ },
++ [11] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "R2Y Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_R2Y_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_r2y_setting),
++ .flags = 0,
++ },
++ [12] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "SAT Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_SAT_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_sat_setting),
++ .flags = 0,
++ },
++ [13] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "SAT Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_SHRP_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_sharp_setting),
++ .flags = 0,
++ },
++ [14] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "YCRV Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_YCRV_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_ycrv_setting),
++ .flags = 0,
++ },
++ [15] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "SC Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_STAT_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_sc_setting),
++ .flags = 0,
++ },
+ };
+
+ static int isp_init_controls(struct stf_isp_dev *isp_dev)
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -1281,6 +1281,22 @@ int video_s_selection(struct file *file,
+ return ret;
+ }
+
++static int stf_video_subscribe_event(struct v4l2_fh *fh,
++ const struct v4l2_event_subscription *sub)
++{
++ switch (sub->type) {
++ case V4L2_EVENT_FRAME_SYNC:
++ return v4l2_event_subscribe(fh, sub, 2, NULL);
++ //int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
++ //pr_info("subscribe ret: %d\n", ret);
++ //return ret;
++ default:
++ return v4l2_ctrl_subscribe_event(fh, sub);
++ //st_debug(ST_VIN, "unsupport subscribe_event\n");
++ //return -EINVAL;
++ }
++}
++
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
+ .vidioc_querycap = video_querycap,
+ .vidioc_enum_fmt_vid_cap = video_enum_fmt,
+@@ -1305,6 +1321,8 @@ static const struct v4l2_ioctl_ops stf_v
+ .vidioc_s_parm = video_s_parm,
+ .vidioc_s_selection = video_s_selection,
+ .vidioc_g_selection = video_g_selection,
++ .vidioc_subscribe_event = stf_video_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ };
+
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
+@@ -1331,6 +1349,8 @@ static const struct v4l2_ioctl_ops stf_v
+ .vidioc_s_parm = video_s_parm,
+ .vidioc_s_selection = video_s_selection,
+ .vidioc_g_selection = video_g_selection,
++ .vidioc_subscribe_event = stf_video_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ };
+
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
+@@ -1350,6 +1370,8 @@ static const struct v4l2_ioctl_ops stf_v
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
++ .vidioc_subscribe_event = stf_video_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ };
+
+ static int video_open(struct file *file)
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -1145,9 +1145,12 @@ static void vin_buffer_done(struct vin_l
+ spin_lock_irqsave(&line->output_lock, flags);
+
+ while ((ready_buf = vin_buf_get_ready(output))) {
+- if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++ //if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++ if (line->id == VIN_LINE_ISP_SCD_Y) {
+ event.u.frame_sync.frame_sequence = output->sequence;
+- v4l2_event_queue(line->subdev.devnode, &event);
++ v4l2_event_queue(&(line->video_out.vdev), &event);
++ //v4l2_event_queue(line->subdev.devnode, &event);
++ //pr_info("----------frame sync-----------\n");
+ }
+
+ ready_buf->vb.vb2_buf.timestamp = ts;
+@@ -1346,7 +1349,10 @@ static int stf_vin_subscribe_event(struc
+ {
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+- return v4l2_event_subscribe(fh, sub, 0, NULL);
++ //return v4l2_event_subscribe(fh, sub, 2, NULL);
++ int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
++ pr_info("subscribe ret: %d\n", ret);
++ return ret;
+ default:
+ st_debug(ST_VIN, "unsupport subscribe_event\n");
+ return -EINVAL;
+@@ -1355,8 +1361,8 @@ static int stf_vin_subscribe_event(struc
+
+ static const struct v4l2_subdev_core_ops vin_core_ops = {
+ .s_power = vin_set_power,
+- .subscribe_event = stf_vin_subscribe_event,
+- .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++ //.subscribe_event = stf_vin_subscribe_event,
++ //.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ };
+
+ static const struct v4l2_subdev_video_ops vin_video_ops = {
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -15,6 +15,8 @@
+
+ #include <linux/v4l2-controls.h>
+
++#define V4L2_CID_USER_JH7110_ISP_BASE (V4L2_CID_USER_BASE + 0x1170)
++
+ #define V4L2_CID_USER_JH7110_ISP_WB_SETTING \
+ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
+ #define V4L2_CID_USER_JH7110_ISP_CAR_SETTING \
+@@ -45,6 +47,8 @@
+ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
+ #define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING \
+ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
++#define V4L2_CID_USER_JH7110_ISP_STAT_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0010)
+
+ struct jh7110_isp_wb_gain {
+ __u16 gain_r;
+@@ -202,13 +206,13 @@ struct jh7110_isp_sat_curve {
+ };
+
+ struct jh7110_isp_sat_hue_info {
+- __s16 sin;
+ __s16 cos;
++ __s16 sin;
+ };
+
+ struct jh7110_isp_sat_info {
+ __s16 gain_cmab;
+- __s16 gain_cmad;
++ __s16 gain_cmmd;
+ __s16 threshold_cmb;
+ __s16 threshold_cmd;
+ __s16 offset_u;
+@@ -230,7 +234,8 @@ struct jh7110_isp_sharp_weight {
+
+ struct jh7110_isp_sharp_strength {
+ __s16 diff[4];
+- __s16 f[4];
++ __s16 f[3];
++ __s32 s[3];
+ };
+
+ struct jh7110_isp_sharp_setting {
+@@ -250,4 +255,41 @@ struct jh7110_isp_ycrv_setting {
+ struct jh7110_isp_ycrv_curve curve;
+ };
+
++struct jh7110_isp_sc_af_config {
++ __u8 es_hor_mode;
++ __u8 es_sum_mode;
++ __u8 hor_en;
++ __u8 ver_en;
++ __u8 es_ver_thr;
++ __u16 es_hor_thr;
++};
++
++struct jh7110_isp_sc_awb_ws {
++ __u8 awb_ws_rl;
++ __u8 awb_ws_ru;
++ __u8 awb_ws_grl;
++ __u8 awb_ws_gru;
++ __u8 awb_ws_gbl;
++ __u8 awb_ws_gbu;
++ __u8 awb_ws_bl;
++ __u8 awb_ws_bu;
++};
++
++struct jh7110_isp_sc_awb_point {
++ __u16 intensity;
++ __u8 weight;
++};
++
++struct jh7110_isp_sc_awb_config {
++ struct jh7110_isp_sc_awb_ws ws_config;
++ __u8 awb_cw[169];
++ struct jh7110_isp_sc_awb_point pts[17];
++};
++
++struct jh7110_isp_sc_setting {
++ __u32 enabled;
++ struct jh7110_isp_sc_af_config af_config;
++ struct jh7110_isp_sc_awb_config awb_config;
++};
++
+ #endif
--- /dev/null
+From 5d61c6fd10144605238311d68c99449c4667a345 Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 7 Aug 2023 10:38:36 +0800
+Subject: [PATCH 095/116] Expand 2 bytes after the SC buffer for the AE/AWB
+ flag and copy the histogram data to the SC buffer.
+
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c | 37 ++++++++++++++++++-
+ .../platform/starfive/v4l2_driver/stf_isp.h | 2 +-
+ .../platform/starfive/v4l2_driver/stf_video.c | 5 ---
+ .../platform/starfive/v4l2_driver/stf_vin.c | 36 +++++++++---------
+ include/uapi/linux/jh7110-isp.h | 33 +++++++++++++++++
+ 5 files changed, 87 insertions(+), 26 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -323,6 +323,13 @@ FILL_ISP_REGS_FUNC(u16);
+ #define FILL_ISP_REGS(type, ispbase, offset, value, size, mask, nbits) \
+ fill_isp_regs_##type(ispbase, offset, value, size, mask, nbits)
+
++static void fill_regs_with_zero(void __iomem *ispbase, u32 offset, u32 size)
++{
++ u32 i;
++ for(i = 0; i < size; i++, offset += 4)
++ reg_write(ispbase, offset, 0);
++}
++
+ static int isp_set_ctrl_wb(struct stf_isp_dev *isp_dev, const void * value)
+ {
+ const struct module_register_info * reg_info = &mod_reg_info[imi_awb];
+@@ -335,6 +342,8 @@ static int isp_set_ctrl_wb(struct stf_is
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+
++ fill_regs_with_zero(ispbase, reg_info->cfg_reg, 16);
++
+ reg_write(ispbase, reg_addr, r_g);
+ reg_write(ispbase, reg_addr + 1 * 4, r_g);
+ reg_write(ispbase, reg_addr + 2 * 4, g_g);
+@@ -370,8 +379,13 @@ static int isp_set_ctrl_ccm(struct stf_i
+ void __iomem *ispbase = vin->isp_base;
+
+ reg_write(ispbase, reg_info->cfg_reg, 6 << 16);
++ fill_regs_with_zero(ispbase, reg_info->cfg_reg + 4, 11);
++
+ FILL_ISP_REGS(u32, ispbase, reg_addr, (u32 *)ccm, 12, 0x7ff, 0);
+
++ reg_addr += 12 * 4;
++ fill_regs_with_zero(ispbase, reg_addr, 2);
++
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+ return 0;
+@@ -640,6 +654,27 @@ static int isp_set_ctrl_sc(struct stf_is
+ u32 * w_diff = weight_cfg + 2;
+ s32 i;
+
++ // SC dumping axi id
++ reg_write(ispbase, 0x9c, 1 << 24);
++
++ // SC frame crop
++ reg_write(ispbase, 0xb8, ((u32)(setting->crop_config.v_start) << 16) | setting->crop_config.h_start);
++
++ // SC config1
++ reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) |
++ ((u32)(setting->crop_config.sw_height) << 8) | setting->crop_config.sw_width);
++
++ // SC decimation config
++ reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) |
++ ((u32)(setting->crop_config.hkeep) << 8) | setting->crop_config.hperiod);
++
++ // SC AWB pixel sum config
++ reg_write(ispbase, 0xc4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_rl, 4, 0xff, 8));
++ reg_write(ispbase, 0xc8, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_bl, 4, 0xff, 8));
++ reg_write(ispbase, 0xcc, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grl, 2, 0xffff, 16));
++ reg_write(ispbase, 0xd0, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_gbl, 2, 0xffff, 16));
++ reg_write(ispbase, 0xd4, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grbl, 2, 0xffff, 16));
++
+ // AF register
+ reg_write(ispbase, 0xc0,
+ ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
+@@ -686,7 +721,7 @@ static int isp_set_ctrl_sc(struct stf_is
+ FILL_ISP_REGS(u32, ispbase, reg_addr, weight_cfg, 6, 0xffffffff, 0);
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+@@ -18,7 +18,7 @@
+
+ #define ISP_SCD_BUFFER_SIZE (19 * 256 * 4) // align 128
+ #define ISP_YHIST_BUFFER_SIZE (64 * 4)
+-#define ISP_SCD_Y_BUFFER_SIZE (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
++#define ISP_SCD_Y_BUFFER_SIZE (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE + 2)
+ #define ISP_RAW_DATA_BITS 12
+ #define SCALER_RATIO_MAX 1 // no compose function
+ #define STF_ISP_REG_OFFSET_MAX 0x0FFF
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -1287,13 +1287,8 @@ static int stf_video_subscribe_event(str
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+- //int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
+- //pr_info("subscribe ret: %d\n", ret);
+- //return ret;
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+- //st_debug(ST_VIN, "unsupport subscribe_event\n");
+- //return -EINVAL;
+ }
+ }
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -1147,6 +1147,17 @@ static void vin_buffer_done(struct vin_l
+ while ((ready_buf = vin_buf_get_ready(output))) {
+ //if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
+ if (line->id == VIN_LINE_ISP_SCD_Y) {
++#define ADDR_REG_YHIST_ACC_0 0x0D00
++ struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++ struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ u32 y_hist_reg_addr = ADDR_REG_YHIST_ACC_0;
++ u32 * y_hist_addr = (u32 *)ready_buf->vaddr_sc;
++ s32 i = 0;
++
++ for(i = 0; i < 64; i++, y_hist_reg_addr += 4)
++ y_hist_addr[i] = reg_read(ispbase, y_hist_reg_addr);
++
+ event.u.frame_sync.frame_sequence = output->sequence;
+ v4l2_event_queue(&(line->video_out.vdev), &event);
+ //v4l2_event_queue(line->subdev.devnode, &event);
+@@ -1246,9 +1257,14 @@ static void vin_change_buffer(struct vin
+ scd_type = vin_dev->hw_ops->vin_isp_get_scd_type(vin_dev);
+ ready_buf->vb.flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
+ if (scd_type == AWB_TYPE)
++ {
+ ready_buf->vb.flags |= V4L2_BUF_FLAG_PFRAME;
+- else
++ *((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0xffff;
++ }else{
+ ready_buf->vb.flags |= V4L2_BUF_FLAG_BFRAME;
++ *((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0;
++ }
++
+ if (!output->frame_skip) {
+ output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
+ scd_type = scd_type == AWB_TYPE ? OECF_TYPE : AWB_TYPE;
+@@ -1343,26 +1359,8 @@ static int vin_link_setup(struct media_e
+ return 0;
+ }
+
+-static int stf_vin_subscribe_event(struct v4l2_subdev *sd,
+- struct v4l2_fh *fh,
+- struct v4l2_event_subscription *sub)
+-{
+- switch (sub->type) {
+- case V4L2_EVENT_FRAME_SYNC:
+- //return v4l2_event_subscribe(fh, sub, 2, NULL);
+- int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
+- pr_info("subscribe ret: %d\n", ret);
+- return ret;
+- default:
+- st_debug(ST_VIN, "unsupport subscribe_event\n");
+- return -EINVAL;
+- }
+-}
+-
+ static const struct v4l2_subdev_core_ops vin_core_ops = {
+ .s_power = vin_set_power,
+- //.subscribe_event = stf_vin_subscribe_event,
+- //.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ };
+
+ static const struct v4l2_subdev_video_ops vin_video_ops = {
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -255,6 +255,17 @@ struct jh7110_isp_ycrv_setting {
+ struct jh7110_isp_ycrv_curve curve;
+ };
+
++struct jh7110_isp_sc_config {
++ __u16 h_start;
++ __u16 v_start;
++ __u8 sw_width;
++ __u8 sw_height;
++ __u8 hperiod;
++ __u8 hkeep;
++ __u8 vperiod;
++ __u8 vkeep;
++};
++
+ struct jh7110_isp_sc_af_config {
+ __u8 es_hor_mode;
+ __u8 es_sum_mode;
+@@ -264,6 +275,23 @@ struct jh7110_isp_sc_af_config {
+ __u16 es_hor_thr;
+ };
+
++struct jh7110_isp_sc_awb_ps {
++ __u8 awb_ps_rl;
++ __u8 awb_ps_ru;
++ __u8 awb_ps_gl;
++ __u8 awb_ps_gu;
++ __u8 awb_ps_bl;
++ __u8 awb_ps_bu;
++ __u8 awb_ps_yl;
++ __u8 awb_ps_yu;
++ __u16 awb_ps_grl;
++ __u16 awb_ps_gru;
++ __u16 awb_ps_gbl;
++ __u16 awb_ps_gbu;
++ __u16 awb_ps_grbl;
++ __u16 awb_ps_grbu;
++};
++
+ struct jh7110_isp_sc_awb_ws {
+ __u8 awb_ws_rl;
+ __u8 awb_ws_ru;
+@@ -275,12 +303,16 @@ struct jh7110_isp_sc_awb_ws {
+ __u8 awb_ws_bu;
+ };
+
++
+ struct jh7110_isp_sc_awb_point {
+ __u16 intensity;
+ __u8 weight;
+ };
+
+ struct jh7110_isp_sc_awb_config {
++ struct jh7110_isp_sc_awb_ps ws_ps_config;
++ __u8 awb_ps_grb_ba;
++ __u8 sel;
+ struct jh7110_isp_sc_awb_ws ws_config;
+ __u8 awb_cw[169];
+ struct jh7110_isp_sc_awb_point pts[17];
+@@ -288,6 +320,7 @@ struct jh7110_isp_sc_awb_config {
+
+ struct jh7110_isp_sc_setting {
+ __u32 enabled;
++ struct jh7110_isp_sc_config crop_config;
+ struct jh7110_isp_sc_af_config af_config;
+ struct jh7110_isp_sc_awb_config awb_config;
+ };
--- /dev/null
+From 04eff0f76091015cbecd39de41d45c493e7a91db Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 30 Oct 2023 16:09:58 +0800
+Subject: [PATCH 096/116] Add ISP control for video2 and video3.
+
+Signed-off-by: zejian.su <zejian.su@starfivetech.com>
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c | 46 +++++++++++++++++++
+ include/uapi/linux/jh7110-isp.h | 23 ++++++++++
+ 2 files changed, 69 insertions(+)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -725,6 +725,24 @@ static int isp_set_ctrl_sc(struct stf_is
+ return 0;
+ }
+
++static int isp_set_ctrl_outss(struct stf_isp_dev *isp_dev, const void * value)
++{
++ const struct jh7110_isp_outss_setting * setting = (const struct jh7110_isp_outss_setting *)value;
++ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++ void __iomem *ispbase = vin->isp_base;
++ u32 reg_addr = !setting->which ? 0xa9c : 0xab4;
++
++ if(!setting->stride)
++ return 0;
++
++ // Output Image Stride Register, 8-byte(64bit) granularity.
++ reg_write(ispbase, reg_addr, setting->stride);
++ reg_write(ispbase, reg_addr + 4, ((setting->hsm << 16) | (setting->hsm & 0x3)));
++ reg_write(ispbase, reg_addr + 8, ((setting->vsm << 16) | (setting->vsm & 0x3)));
++
++ return 0;
++}
++
+ static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+@@ -822,6 +840,10 @@ static int isp_s_ctrl(struct v4l2_ctrl *
+ case V4L2_CID_USER_JH7110_ISP_STAT_SETTING:
+ ret = isp_set_ctrl_sc(isp_dev, ctrl->p_new.p_u8);
+ break;
++ case V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING:
++ case V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING:
++ ret = isp_set_ctrl_outss(isp_dev, ctrl->p_new.p_u8);
++ break;
+ default:
+ ret = -EINVAL;
+ break;
+@@ -1028,6 +1050,30 @@ struct v4l2_ctrl_config isp_ctrl[] = {
+ .dims[0] = sizeof(struct jh7110_isp_sc_setting),
+ .flags = 0,
+ },
++ [16] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "OUTSS Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_outss_setting),
++ .flags = 0,
++ },
++ [17] = {
++ .ops = &isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ .name = "OUTSS Setting",
++ .id = V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING,
++ .dims[0] = sizeof(struct jh7110_isp_outss_setting),
++ .flags = 0,
++ },
+ };
+
+ static int isp_init_controls(struct stf_isp_dev *isp_dev)
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -49,6 +49,10 @@
+ (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
+ #define V4L2_CID_USER_JH7110_ISP_STAT_SETTING \
+ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0010)
++#define V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0011)
++#define V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING \
++ (V4L2_CID_USER_JH7110_ISP_BASE + 0x0012)
+
+ struct jh7110_isp_wb_gain {
+ __u16 gain_r;
+@@ -325,4 +329,23 @@ struct jh7110_isp_sc_setting {
+ struct jh7110_isp_sc_awb_config awb_config;
+ };
+
++struct jh7110_isp_outss_setting {
++ __u8 which;
++ __u16 stride; // Output Image Stride Register, 8-byte(64bit) granularity.
++ __u8 hsm; // horizontal scale mode
++ __u32 hsf; // horizontal scale factor (time 4096)
++ __u8 vsm; // vertical scale mode
++ __u32 vsf; // vertical scale factor (time 4096)
++};
++
++struct jh7110_isp_sc_buffer {
++ __u32 y_histogram[64];
++ __u32 reserv0[33];
++ __u32 bright_sc[4096];
++ __u32 reserv1[96];
++ __u32 ae_hist_y[128];
++ __u32 reserv2[511];
++ __u16 flag;
++};
++
+ #endif
--- /dev/null
+From 883745d4728e524babfe7d04cbb8a925c22aa6b5 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 13 Nov 2023 14:46:50 +0800
+Subject: [PATCH 097/116] media: starfive: Update ISP initialzation
+
+ISP compatible stf_isp_ctrl and libcamera.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c | 55 ++++++++-----------
+ 1 file changed, 24 insertions(+), 31 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -402,7 +402,7 @@ static int isp_set_ctrl_cfa(struct stf_i
+
+ reg_write(ispbase, reg_addr, ((u32)(cfg->cross_cov & 0x3) << 4) | (cfg->hv_width & 0xf));
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -419,7 +419,7 @@ static int isp_set_ctrl_ctc(struct stf_i
+ reg_value |= ((u32)(cfg->max_gt & 0x3ff) << 16) | (cfg->min_gt & 0x3ff);
+ reg_write(ispbase, reg_addr, reg_value);
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -434,7 +434,7 @@ static int isp_set_ctrl_dbc(struct stf_i
+
+ reg_write(ispbase, reg_addr, ((u32)(cfg->bad_gt & 0x3ff) << 16) | (cfg->bad_xt & 0x3ff));
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -459,7 +459,7 @@ static int isp_set_ctrl_dnyuv(struct stf
+ reg_write(ispbase, reg_addr + 36, CREATE_REG_VALUE(u16, &cfg->uv_curve[4], 2, 0x3ff, 16));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -472,7 +472,7 @@ static int isp_set_ctrl_gmargb(struct st
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ for(i = 0; i < 15; i++, curve++, reg_addr += 4)
+ reg_write(ispbase, reg_addr, ((u32)curve->sg_val << 16) | (curve->g_val & 0x3ff));
+
+@@ -490,7 +490,7 @@ static int isp_set_ctrl_lccf(struct stf_
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ reg_write(ispbase, reg_addr, ((u32)(setting->circle.center_y & 0x7fff) << 16) | (setting->circle.center_x & 0x7fff));
+ reg_write(ispbase, reg_addr + 8, setting->circle.radius & 0xf);
+ reg_addr += 0x90;
+@@ -498,7 +498,7 @@ static int isp_set_ctrl_lccf(struct stf_
+ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x1fff, 16));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -511,7 +511,7 @@ static int isp_set_ctrl_obc(struct stf_i
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ reg_write(ispbase, reg_addr, ((u32)(setting->win_size.height & 0xf) << 4) | (setting->win_size.width & 0xf));
+
+ reg_addr += 0x2ac; //0x2e0
+@@ -519,7 +519,7 @@ static int isp_set_ctrl_obc(struct stf_i
+ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 4, 0xff, 8));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -535,7 +535,7 @@ static int isp_set_ctrl_oecf(struct stf_
+ void __iomem *ispbase = vin->isp_base;
+ u32 x, y, slope;
+ s32 i;
+-
++
+ for(i = 0; i < 32; i++, reg_x_addr += 4, reg_y_addr += 4, reg_s_addr += 4) {
+ x = pts->x & 0x3ff;
+ y = pts->y & 0x3ff;
+@@ -565,12 +565,12 @@ static int isp_set_ctrl_r2y(struct stf_i
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ for(i = 0; i < 9; i++, reg_addr += 4)
+ reg_write(ispbase, reg_addr, (u32)(params[i] & 0x1ff));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -584,7 +584,7 @@ static int isp_set_ctrl_sat(struct stf_i
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ for(i = 0; i < 3; i++, reg_addr += 4, params += 2)
+ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0xfff, 16));
+ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, &setting->hue_info.cos, 2, 0x3ff, 16));
+@@ -597,7 +597,7 @@ static int isp_set_ctrl_sat(struct stf_i
+ reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x3fff, 16));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -609,7 +609,7 @@ static int isp_set_ctrl_shrp(struct stf_
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+ s32 i;
+-
++
+ for(i = 0; i < 4; i++, reg_addr += 4)
+ reg_write(ispbase, reg_addr, ((u32)(setting->strength.diff[i] & 0x3ff) << 16) | ((u32)(setting->weight.weight[i] & 0xf) << 8));
+ FILL_ISP_REGS(u8, ispbase, reg_addr, (u8 *)(&setting->weight.weight[4]), 15 - 4, 0xf, 8);
+@@ -622,7 +622,7 @@ static int isp_set_ctrl_shrp(struct stf_
+ reg_write(ispbase, reg_addr, ((u32)(setting->pdirf & 0xf) << 28) | ((u32)(setting->ndirf & 0xf) << 24) | (setting->weight.recip_wei_sum & 0x3fffff));
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -633,11 +633,11 @@ static int isp_set_ctrl_ycrv(struct stf_
+ u32 reg_addr = reg_info->cfg_reg;
+ struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+ void __iomem *ispbase = vin->isp_base;
+-
++
+ FILL_ISP_REGS(u16, ispbase, reg_addr, (u16 *)(setting->curve.y), 64, 0x3ff, 0);
+
+ reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-
++
+ return 0;
+ }
+
+@@ -661,11 +661,11 @@ static int isp_set_ctrl_sc(struct stf_is
+ reg_write(ispbase, 0xb8, ((u32)(setting->crop_config.v_start) << 16) | setting->crop_config.h_start);
+
+ // SC config1
+- reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) |
++ reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) |
+ ((u32)(setting->crop_config.sw_height) << 8) | setting->crop_config.sw_width);
+
+ // SC decimation config
+- reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) |
++ reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) |
+ ((u32)(setting->crop_config.hkeep) << 8) | setting->crop_config.hperiod);
+
+ // SC AWB pixel sum config
+@@ -676,7 +676,7 @@ static int isp_set_ctrl_sc(struct stf_is
+ reg_write(ispbase, 0xd4, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grbl, 2, 0xffff, 16));
+
+ // AF register
+- reg_write(ispbase, 0xc0,
++ reg_write(ispbase, 0xc0,
+ ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
+ ((u32)(setting->af_config.es_ver_thr & 0xff) << 8) |
+ ((setting->af_config.ver_en & 0x1) << 3) |
+@@ -1217,7 +1217,7 @@ static int isp_get_interface_type(struct
+ static int isp_set_stream(struct v4l2_subdev *sd, int enable)
+ {
+ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+- int ret = 0, interface_type;
++ int interface_type;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_event src_ch = { 0 };
+
+@@ -1225,6 +1225,7 @@ static int isp_set_stream(struct v4l2_su
+ mutex_lock(&isp_dev->stream_lock);
+ if (enable) {
+ if (isp_dev->stream_count == 0) {
++ v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
+ isp_dev->hw_ops->isp_clk_enable(isp_dev);
+ if (!user_config_isp)
+ isp_dev->hw_ops->isp_config_set(isp_dev);
+@@ -1256,15 +1257,7 @@ static int isp_set_stream(struct v4l2_su
+ exit:
+ mutex_unlock(&isp_dev->stream_lock);
+
+- mutex_lock(&isp_dev->power_lock);
+- /* restore controls */
+- if (enable && isp_dev->power_count == 1) {
+- mutex_unlock(&isp_dev->power_lock);
+- ret = v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
+- } else
+- mutex_unlock(&isp_dev->power_lock);
+-
+- return ret;
++ return 0;
+ }
+
+ /*Try to match sensor format with sink, and then get the index as default.*/
--- /dev/null
+From 02f84ff43453b0f8dc2a1e4885e7003929349ee6 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 5 Mar 2024 11:00:21 +0800
+Subject: [PATCH 098/116] crypto: jh7110: Comment RSA algo register
+
+There are some issues in RSA algo, which will cause kernel crash.
+So comment RSA algo register temporarily.
+This commit should be reverted after the RSA issues are fixed.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/crypto/starfive/jh7110-cryp.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-cryp.c
++++ b/drivers/crypto/starfive/jh7110-cryp.c
+@@ -194,14 +194,14 @@ static int starfive_cryp_probe(struct pl
+ if (ret)
+ goto err_algs_hash;
+
+- ret = starfive_rsa_register_algs();
+- if (ret)
+- goto err_algs_rsa;
++// ret = starfive_rsa_register_algs();
++// if (ret)
++// goto err_algs_rsa;
+
+ return 0;
+
+-err_algs_rsa:
+- starfive_hash_unregister_algs();
++// err_algs_rsa:
++// starfive_hash_unregister_algs();
+ err_algs_hash:
+ starfive_aes_unregister_algs();
+ err_algs_aes:
--- /dev/null
+From a7ff2b51e2941526d6924dcf8f1760187d7e5d03 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 11 Mar 2024 11:10:45 +0800
+Subject: [PATCH 099/116] riscv: dts: starfive: jh7110-evb: Add qspi norflash
+ partition for uboot-env
+
+Add qspi norflash partition "uboot-env@f0000",
+for synchronizing with other branches.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+@@ -568,6 +568,9 @@
+ spl@0 {
+ reg = <0x0 0x40000>;
+ };
++ uboot-env@f0000 {
++ reg = <0xf0000 0x10000>;
++ };
+ uboot@100000 {
+ reg = <0x100000 0x300000>;
+ };
--- /dev/null
+From f96ba2eb39ba950f67edf43e0c5c88ac660bc2a0 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:43:27 +0800
+Subject: [PATCH 100/116] driver: regulator: pmic driver support kernel 6.6
+
+pmic driver support kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ drivers/regulator/axp20x-regulator.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/regulator/axp20x-regulator.c
++++ b/drivers/regulator/axp20x-regulator.c
+@@ -20,6 +20,7 @@
+ #include <linux/mfd/axp20x.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
++#include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/driver.h>
--- /dev/null
+From c609818850807a1ae5fa17e165f2b66b914188b4 Mon Sep 17 00:00:00 2001
+From: "xingyu.wu" <xingyu.wu@starfivetech.com>
+Date: Tue, 28 Jun 2022 22:48:15 +0800
+Subject: [PATCH 101/116] spi-pl022:starfive:Add platform bus register to adapt
+ overlay
+
+Add platform bus register to adapt dtbo overlay.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 137 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 131 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -34,6 +34,7 @@
+ #include <linux/of.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/reset.h>
++#include <linux/platform_device.h>
+
+ /*
+ * This macro is used to define some register default values.
+@@ -2088,7 +2089,10 @@ pl022_platform_data_dt_get(struct device
+ return NULL;
+ }
+
+- pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
++ if (strncmp(dev->bus->name, "platform", strlen("platform")))
++ pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
++ else
++ pd = kzalloc(sizeof(struct pl022_ssp_controller), GFP_KERNEL);
+ if (!pd)
+ return NULL;
+
+@@ -2108,6 +2112,14 @@ static int pl022_probe(struct amba_devic
+ struct spi_controller *host;
+ struct pl022 *pl022 = NULL; /*Data for this driver */
+ int status = 0;
++ int platform_flag = 0;
++
++ if (strncmp(dev->bus->name, "platform", strlen("platform")))
++ platform_flag = 0;
++ else
++ platform_flag = 1;
++ dev_dbg(&adev->dev, "bus name:%s platform flag:%d",
++ dev->bus->name, platform_flag);
+
+ dev_info(&adev->dev,
+ "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+@@ -2161,7 +2173,11 @@ static int pl022_probe(struct amba_devic
+ goto err_no_ioregion;
+
+ pl022->phybase = adev->res.start;
+- pl022->virtbase = devm_ioremap(dev, adev->res.start,
++ if (platform_flag)
++ pl022->virtbase = ioremap(adev->res.start,
++ resource_size(&adev->res));
++ else
++ pl022->virtbase = devm_ioremap(dev, adev->res.start,
+ resource_size(&adev->res));
+ if (pl022->virtbase == NULL) {
+ status = -ENOMEM;
+@@ -2170,7 +2186,10 @@ static int pl022_probe(struct amba_devic
+ dev_info(&adev->dev, "mapped registers from %pa to %p\n",
+ &adev->res.start, pl022->virtbase);
+
+- pl022->clk = devm_clk_get(&adev->dev, NULL);
++ if (platform_flag)
++ pl022->clk = clk_get(&adev->dev, NULL);
++ else
++ pl022->clk = devm_clk_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->clk)) {
+ status = PTR_ERR(pl022->clk);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+@@ -2183,7 +2202,10 @@ static int pl022_probe(struct amba_devic
+ goto err_no_clk_en;
+ }
+
+- pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++ if (platform_flag)
++ pl022->rst = reset_control_get_exclusive(&adev->dev, NULL);
++ else
++ pl022->rst = devm_reset_control_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->rst)) {
+ status = PTR_ERR(pl022->rst);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
+@@ -2205,7 +2227,11 @@ static int pl022_probe(struct amba_devic
+ SSP_CR1(pl022->virtbase));
+ load_ssp_default_config(pl022);
+
+- status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
++ if (platform_flag)
++ status = request_irq(adev->irq[0], pl022_interrupt_handler,
++ 0, "pl022", pl022);
++ else
++ status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
+ 0, "pl022", pl022);
+ if (status < 0) {
+ dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+@@ -2230,7 +2256,10 @@ static int pl022_probe(struct amba_devic
+
+ /* Register with the SPI framework */
+ amba_set_drvdata(adev, pl022);
+- status = devm_spi_register_controller(&adev->dev, host);
++ if (platform_flag)
++ status = spi_register_controller(host);
++ else
++ status = devm_spi_register_controller(&adev->dev, host);
+ if (status != 0) {
+ dev_err_probe(&adev->dev, status,
+ "problem registering spi host\n");
+@@ -2255,15 +2284,26 @@ static int pl022_probe(struct amba_devic
+ if (platform_info->enable_dma)
+ pl022_dma_remove(pl022);
+ err_no_irq:
++ if (platform_flag)
++ free_irq(adev->irq[0], pl022);
++ reset_control_assert(pl022->rst);
+ err_no_rst_de:
++ if (platform_flag)
++ reset_control_put(pl022->rst);
+ err_no_rst:
+ clk_disable_unprepare(pl022->clk);
+ err_no_clk_en:
++ if (platform_flag)
++ clk_put(pl022->clk);
+ err_no_clk:
++ if (platform_flag)
++ iounmap(pl022->virtbase);
+ err_no_ioremap:
+ amba_release_regions(adev);
+ err_no_ioregion:
+ spi_controller_put(host);
++ if (platform_flag)
++ kfree(platform_info);
+ return status;
+ }
+
+@@ -2464,6 +2504,91 @@ static void __exit pl022_exit(void)
+ }
+ module_exit(pl022_exit);
+
++/*
++ * Register PL022 in platform bus to accommodate overlay use.
++ * Because overlay only trigger response from the platform bus
++ * not amba bus.
++ */
++static int starfive_of_pl022_probe(struct platform_device *pdev)
++{
++ int ret;
++ const struct amba_id id = {
++ .id = 0x00041022,
++ .mask = 0x000fffff,
++ .data = &vendor_arm
++ };
++ struct amba_device *pcdev;
++ struct device *dev = &pdev->dev;
++
++ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
++ if (!pcdev)
++ return -ENOMEM;
++
++ pcdev->dev = pdev->dev;
++ pcdev->periphid = id.id;
++ pcdev->res = *(pdev->resource);
++
++ pcdev->irq[0] = platform_get_irq(pdev, 0);
++ if (pcdev->irq[0] < 0) {
++ dev_err(dev, "failed to get irq\n");
++ ret = -EINVAL;
++ }
++
++ ret = pl022_probe(pcdev, &id);
++
++ return ret;
++}
++
++static int starfive_of_pl022_remove(struct platform_device *pdev)
++{
++ u32 size;
++ int irq;
++ struct pl022 *pl022 = dev_get_drvdata(&pdev->dev);
++
++ if (!pl022)
++ return 0;
++
++ pm_runtime_get_noresume(&pdev->dev);
++
++ load_ssp_default_config(pl022);
++ if (pl022->host_info->enable_dma)
++ pl022_dma_remove(pl022);
++
++ irq = platform_get_irq(pdev, 0);
++ free_irq(irq, pl022);
++ reset_control_assert(pl022->rst);
++ reset_control_put(pl022->rst);
++ clk_disable_unprepare(pl022->clk);
++ clk_put(pl022->clk);
++ iounmap(pl022->virtbase);
++ kfree(pl022->host_info);
++
++ size = resource_size(pdev->resource);
++ release_mem_region(pdev->resource->start, size);
++ tasklet_disable(&pl022->pump_transfers);
++ return 0;
++}
++
++static const struct of_device_id starfive_of_pl022_match[] = {
++ { .compatible = "starfive,jh7110-spi-pl022" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, starfive_of_pl022_match);
++
++static struct platform_driver starfive_of_pl022_driver = {
++ .driver = {
++ .name = "starfive-spi-pl022",
++ .of_match_table = starfive_of_pl022_match,
++ .pm = &pl022_dev_pm_ops,
++ },
++ .probe = starfive_of_pl022_probe,
++ .remove = starfive_of_pl022_remove,
++};
++
++module_platform_driver(starfive_of_pl022_driver);
++/* platform register end */
++
++MODULE_AUTHOR("xingyu.wu <xingyu.wu@starfivetech.com>");
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+ MODULE_DESCRIPTION("PL022 SSP Controller Driver");
+ MODULE_LICENSE("GPL");
--- /dev/null
+From f94a07310f18720faa0cc773a1aec5a7b1bf3928 Mon Sep 17 00:00:00 2001
+From: "xingyu.wu" <xingyu.wu@starfivetech.com>
+Date: Tue, 19 Jul 2022 14:49:20 +0800
+Subject: [PATCH 102/116] spi:pl022-starfive:Avoid power device error when
+ CONFIG_PM enable
+
+It would be error when CONFIG_PM enable and use overlay by of-platform to register.
+
+Add some power manager operation in platform probe function.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 36 ++++++++++++++++++++++++++++++++++--
+ 1 file changed, 34 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -35,6 +35,8 @@
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/reset.h>
+ #include <linux/platform_device.h>
++#include <linux/clk/clk-conf.h>
++#include <linux/pm_domain.h>
+
+ /*
+ * This macro is used to define some register default values.
+@@ -2266,7 +2268,8 @@ static int pl022_probe(struct amba_devic
+ goto err_spi_register;
+ }
+ dev_dbg(dev, "probe succeeded\n");
+-
++ if (!platform_flag)
++ platform_info->autosuspend_delay = 100;
+ /* let runtime pm put suspend */
+ if (platform_info->autosuspend_delay > 0) {
+ dev_info(&adev->dev,
+@@ -2276,7 +2279,10 @@ static int pl022_probe(struct amba_devic
+ platform_info->autosuspend_delay);
+ pm_runtime_use_autosuspend(dev);
+ }
+- pm_runtime_put(dev);
++ if (platform_flag)
++ clk_disable_unprepare(pl022->clk);
++ else
++ pm_runtime_put(dev);
+
+ return 0;
+
+@@ -2534,8 +2540,33 @@ static int starfive_of_pl022_probe(struc
+ ret = -EINVAL;
+ }
+
++ ret = of_clk_set_defaults(dev->of_node, false);
++ if (ret < 0)
++ goto err_probe;
++
++ ret = dev_pm_domain_attach(dev, true);
++ if (ret)
++ goto err_probe;
++
+ ret = pl022_probe(pcdev, &id);
+
++ struct pl022 *pl022 = amba_get_drvdata(pcdev);
++
++ pl022->host->dev.parent = &pdev->dev;
++ platform_set_drvdata(pdev, pl022);
++
++ pm_runtime_enable(&pdev->dev);
++ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
++ pm_runtime_use_autosuspend(&pdev->dev);
++
++ if (ret) {
++ pm_runtime_disable(dev);
++ pm_runtime_set_suspended(dev);
++ pm_runtime_put_noidle(dev);
++ dev_pm_domain_detach(dev, true);
++ }
++
++err_probe:
+ return ret;
+ }
+
+@@ -2566,6 +2597,7 @@ static int starfive_of_pl022_remove(stru
+ size = resource_size(pdev->resource);
+ release_mem_region(pdev->resource->start, size);
+ tasklet_disable(&pl022->pump_transfers);
++ pm_runtime_disable(&pdev->dev);
+ return 0;
+ }
+
--- /dev/null
+From 733e7bd23a1efad15a724fbdbce8d9f06aa6813a Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Wed, 23 Nov 2022 14:53:58 +0800
+Subject: [PATCH 103/116] spi-pl022-starfive:fix the problem of spi overlay
+ reload
+
+fix the problem of spi overlay reload
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 270 ++++++++++++++++++++++++++++------------
+ 1 file changed, 188 insertions(+), 82 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -2106,6 +2106,172 @@ pl022_platform_data_dt_get(struct device
+ return pd;
+ }
+
++static int pl022_platform_probe(struct platform_device *pdev, const struct amba_id *id)
++{
++ struct device *dev = &pdev->dev;
++ struct spi_controller *host;
++ struct pl022_ssp_controller *platform_info;
++ struct amba_device *adev;
++ struct pl022 *pl022 = NULL;
++ struct resource *res;
++ int status = 0;
++ int irq;
++
++ dev_info(dev,
++ "ARM PL022 driver for StarFive SoC platform, device ID: 0x%08x\n",
++ id->id);
++
++ adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
++ adev->dev = pdev->dev;
++ platform_info = pl022_platform_data_dt_get(dev);
++ if (!platform_info) {
++ dev_err(dev, "probe: no platform data defined\n");
++ return -ENODEV;
++ }
++ /* Allocate host with space for data */
++ host = spi_alloc_host(dev, sizeof(struct pl022));
++ if (host == NULL) {
++ dev_err(dev, "probe - cannot alloc SPI host\n");
++ return -ENOMEM;
++ }
++
++ pl022 = spi_controller_get_devdata(host);
++ pl022->host = host;
++ pl022->host_info = platform_info;
++ pl022->adev = adev;
++ pl022->vendor = id->data;
++ pl022->host->dev.parent = &pdev->dev;
++ /*
++ * Bus Number Which has been Assigned to this SSP controller
++ * on this board
++ */
++ host->bus_num = platform_info->bus_id;
++ host->cleanup = pl022_cleanup;
++ host->setup = pl022_setup;
++ /* If open CONFIG_PM, auto_runtime_pm should be false when of-platform.*/
++ host->auto_runtime_pm = true;
++ host->transfer_one_message = pl022_transfer_one_message;
++ host->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
++ host->rt = platform_info->rt;
++ host->dev.of_node = dev->of_node;
++ host->use_gpio_descriptors = true;
++
++ /*
++ * Supports mode 0-3, loopback, and active low CS. Transfers are
++ * always MS bit first on the original pl022.
++ */
++ host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
++ if (pl022->vendor->extended_cr)
++ host->mode_bits |= SPI_LSB_FIRST;
++
++ dev_dbg(dev, "BUSNO: %d\n", host->bus_num);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pl022->phybase = res->start;
++ pl022->virtbase = devm_ioremap_resource(dev, res);
++ if (pl022->virtbase == NULL) {
++ status = -ENOMEM;
++ goto err_no_ioremap;
++ }
++ dev_info(dev, "mapped registers from %llx to %llx\n",
++ pdev->resource->start, pdev->resource->end);
++
++ pl022->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(pl022->clk)) {
++ status = PTR_ERR(pl022->clk);
++ dev_err(dev, "could not retrieve SSP/SPI bus clock\n");
++ goto err_no_clk;
++ }
++ status = clk_prepare_enable(pl022->clk);
++ if (status) {
++ dev_err(dev, "could not enable SSP/SPI bus clock\n");
++ goto err_no_clk_en;
++ }
++
++ pl022->rst = devm_reset_control_get_exclusive(dev, NULL);
++ if (!IS_ERR(pl022->rst)) {
++ status = reset_control_deassert(pl022->rst);
++ if (status) {
++ dev_err(dev, "could not deassert SSP/SPI bus reset\n");
++ goto err_no_rst_clr;
++ }
++ } else {
++ status = PTR_ERR(pl022->rst);
++ dev_err(dev, "could not retrieve SSP/SPI bus reset\n");
++ goto err_no_rst;
++ }
++
++ /* Initialize transfer pump */
++ tasklet_init(&pl022->pump_transfers, pump_transfers,
++ (unsigned long)pl022);
++
++ /* Disable SSP */
++ writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
++ SSP_CR1(pl022->virtbase));
++ load_ssp_default_config(pl022);
++
++ /* Obtain IRQ line. */
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ status = -ENXIO;
++ goto err_no_irq;
++ }
++ status = devm_request_irq(dev, irq, pl022_interrupt_handler,
++ 0, "pl022", pl022);
++ if (status < 0) {
++ dev_err(dev, "probe - cannot get IRQ (%d)\n", status);
++ goto err_no_irq;
++ }
++
++ /* Get DMA channels, try autoconfiguration first */
++ status = pl022_dma_autoprobe(pl022);
++ if (status == -EPROBE_DEFER) {
++ dev_dbg(dev, "deferring probe to get DMA channel\n");
++ goto err_no_irq;
++ }
++
++ /* dma is not used unless configured in the device tree */
++ platform_info->enable_dma = 0;
++
++ /* If that failed, use channels from platform_info */
++ if (status == 0)
++ platform_info->enable_dma = 1;
++ else if (platform_info->enable_dma) {
++ status = pl022_dma_probe(pl022);
++ if (status != 0)
++ platform_info->enable_dma = 0;
++ }
++
++ /* Register with the SPI framework */
++ dev_set_drvdata(dev, pl022);
++
++ status = devm_spi_register_controller(dev, host);
++ if (status != 0) {
++ dev_err(dev,
++ "probe - problem registering spi host\n");
++ goto err_spi_register;
++ }
++ dev_dbg(dev, "probe succeeded\n");
++
++ clk_disable_unprepare(pl022->clk);
++
++ return 0;
++ err_spi_register:
++ if (platform_info->enable_dma)
++ pl022_dma_remove(pl022);
++ err_no_irq:
++ reset_control_assert(pl022->rst);
++ err_no_rst_clr:
++ err_no_rst:
++ clk_disable_unprepare(pl022->clk);
++ err_no_clk_en:
++ err_no_clk:
++ err_no_ioremap:
++ release_mem_region(pdev->resource->start, resource_size(pdev->resource));
++ spi_controller_put(host);
++ return status;
++}
++
+ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
+ {
+ struct device *dev = &adev->dev;
+@@ -2114,14 +2280,6 @@ static int pl022_probe(struct amba_devic
+ struct spi_controller *host;
+ struct pl022 *pl022 = NULL; /*Data for this driver */
+ int status = 0;
+- int platform_flag = 0;
+-
+- if (strncmp(dev->bus->name, "platform", strlen("platform")))
+- platform_flag = 0;
+- else
+- platform_flag = 1;
+- dev_dbg(&adev->dev, "bus name:%s platform flag:%d",
+- dev->bus->name, platform_flag);
+
+ dev_info(&adev->dev,
+ "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+@@ -2175,11 +2333,7 @@ static int pl022_probe(struct amba_devic
+ goto err_no_ioregion;
+
+ pl022->phybase = adev->res.start;
+- if (platform_flag)
+- pl022->virtbase = ioremap(adev->res.start,
+- resource_size(&adev->res));
+- else
+- pl022->virtbase = devm_ioremap(dev, adev->res.start,
++ pl022->virtbase = devm_ioremap(dev, adev->res.start,
+ resource_size(&adev->res));
+ if (pl022->virtbase == NULL) {
+ status = -ENOMEM;
+@@ -2188,10 +2342,7 @@ static int pl022_probe(struct amba_devic
+ dev_info(&adev->dev, "mapped registers from %pa to %p\n",
+ &adev->res.start, pl022->virtbase);
+
+- if (platform_flag)
+- pl022->clk = clk_get(&adev->dev, NULL);
+- else
+- pl022->clk = devm_clk_get(&adev->dev, NULL);
++ pl022->clk = devm_clk_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->clk)) {
+ status = PTR_ERR(pl022->clk);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+@@ -2204,10 +2355,7 @@ static int pl022_probe(struct amba_devic
+ goto err_no_clk_en;
+ }
+
+- if (platform_flag)
+- pl022->rst = reset_control_get_exclusive(&adev->dev, NULL);
+- else
+- pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++ pl022->rst = devm_reset_control_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->rst)) {
+ status = PTR_ERR(pl022->rst);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
+@@ -2229,11 +2377,7 @@ static int pl022_probe(struct amba_devic
+ SSP_CR1(pl022->virtbase));
+ load_ssp_default_config(pl022);
+
+- if (platform_flag)
+- status = request_irq(adev->irq[0], pl022_interrupt_handler,
+- 0, "pl022", pl022);
+- else
+- status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
++ status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
+ 0, "pl022", pl022);
+ if (status < 0) {
+ dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+@@ -2258,18 +2402,16 @@ static int pl022_probe(struct amba_devic
+
+ /* Register with the SPI framework */
+ amba_set_drvdata(adev, pl022);
+- if (platform_flag)
+- status = spi_register_controller(host);
+- else
+- status = devm_spi_register_controller(&adev->dev, host);
++
++ status = devm_spi_register_controller(&adev->dev, host);
+ if (status != 0) {
+ dev_err_probe(&adev->dev, status,
+ "problem registering spi host\n");
+ goto err_spi_register;
+ }
+ dev_dbg(dev, "probe succeeded\n");
+- if (!platform_flag)
+- platform_info->autosuspend_delay = 100;
++
++ platform_info->autosuspend_delay = 100;
+ /* let runtime pm put suspend */
+ if (platform_info->autosuspend_delay > 0) {
+ dev_info(&adev->dev,
+@@ -2279,10 +2421,8 @@ static int pl022_probe(struct amba_devic
+ platform_info->autosuspend_delay);
+ pm_runtime_use_autosuspend(dev);
+ }
+- if (platform_flag)
+- clk_disable_unprepare(pl022->clk);
+- else
+- pm_runtime_put(dev);
++
++ pm_runtime_put(dev);
+
+ return 0;
+
+@@ -2290,26 +2430,17 @@ static int pl022_probe(struct amba_devic
+ if (platform_info->enable_dma)
+ pl022_dma_remove(pl022);
+ err_no_irq:
+- if (platform_flag)
+- free_irq(adev->irq[0], pl022);
+ reset_control_assert(pl022->rst);
+ err_no_rst_de:
+- if (platform_flag)
+- reset_control_put(pl022->rst);
+ err_no_rst:
+ clk_disable_unprepare(pl022->clk);
+ err_no_clk_en:
+- if (platform_flag)
+- clk_put(pl022->clk);
+ err_no_clk:
+- if (platform_flag)
+- iounmap(pl022->virtbase);
+ err_no_ioremap:
+ amba_release_regions(adev);
+ err_no_ioregion:
+ spi_controller_put(host);
+- if (platform_flag)
+- kfree(platform_info);
++
+ return status;
+ }
+
+@@ -2523,23 +2654,8 @@ static int starfive_of_pl022_probe(struc
+ .mask = 0x000fffff,
+ .data = &vendor_arm
+ };
+- struct amba_device *pcdev;
+ struct device *dev = &pdev->dev;
+
+- pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+- if (!pcdev)
+- return -ENOMEM;
+-
+- pcdev->dev = pdev->dev;
+- pcdev->periphid = id.id;
+- pcdev->res = *(pdev->resource);
+-
+- pcdev->irq[0] = platform_get_irq(pdev, 0);
+- if (pcdev->irq[0] < 0) {
+- dev_err(dev, "failed to get irq\n");
+- ret = -EINVAL;
+- }
+-
+ ret = of_clk_set_defaults(dev->of_node, false);
+ if (ret < 0)
+ goto err_probe;
+@@ -2548,16 +2664,11 @@ static int starfive_of_pl022_probe(struc
+ if (ret)
+ goto err_probe;
+
+- ret = pl022_probe(pcdev, &id);
++ ret = pl022_platform_probe(pdev, &id);
+
+- struct pl022 *pl022 = amba_get_drvdata(pcdev);
+-
+- pl022->host->dev.parent = &pdev->dev;
+- platform_set_drvdata(pdev, pl022);
+-
+- pm_runtime_enable(&pdev->dev);
+- pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+- pm_runtime_use_autosuspend(&pdev->dev);
++ pm_runtime_enable(dev);
++ pm_runtime_set_autosuspend_delay(dev, 100);
++ pm_runtime_use_autosuspend(dev);
+
+ if (ret) {
+ pm_runtime_disable(dev);
+@@ -2572,32 +2683,27 @@ err_probe:
+
+ static int starfive_of_pl022_remove(struct platform_device *pdev)
+ {
+- u32 size;
+- int irq;
+ struct pl022 *pl022 = dev_get_drvdata(&pdev->dev);
+
+ if (!pl022)
+ return 0;
+
++ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ load_ssp_default_config(pl022);
+ if (pl022->host_info->enable_dma)
+ pl022_dma_remove(pl022);
+
+- irq = platform_get_irq(pdev, 0);
+- free_irq(irq, pl022);
+- reset_control_assert(pl022->rst);
+- reset_control_put(pl022->rst);
+ clk_disable_unprepare(pl022->clk);
+- clk_put(pl022->clk);
+- iounmap(pl022->virtbase);
+- kfree(pl022->host_info);
+-
+- size = resource_size(pdev->resource);
+- release_mem_region(pdev->resource->start, size);
+ tasklet_disable(&pl022->pump_transfers);
++
++ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
++ pm_runtime_set_suspended(&pdev->dev);
++ pm_runtime_put_noidle(&pdev->dev);
++ dev_pm_domain_detach(&pdev->dev, true);
++
+ return 0;
+ }
+
--- /dev/null
+From 11a917bf429a5714e34584f90ab1ac376d399d8f Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Wed, 18 Jan 2023 15:50:47 +0800
+Subject: [PATCH 104/116] spi-pl022-starfive:Enable spi to be compiled into
+ modules
+
+Enable spi to be compiled into modules
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -2633,7 +2633,11 @@ static int __init pl022_init(void)
+ {
+ return amba_driver_register(&pl022_driver);
+ }
++#if !IS_MODULE(CONFIG_SPI_PL022)
+ subsys_initcall(pl022_init);
++#else
++module_init(pl022_init);
++#endif
+
+ static void __exit pl022_exit(void)
+ {
+@@ -2723,7 +2727,9 @@ static struct platform_driver starfive_o
+ .remove = starfive_of_pl022_remove,
+ };
+
++#if !IS_MODULE(CONFIG_SPI_PL022)
+ module_platform_driver(starfive_of_pl022_driver);
++#endif
+ /* platform register end */
+
+ MODULE_AUTHOR("xingyu.wu <xingyu.wu@starfivetech.com>");
--- /dev/null
+From e7b01c0a9ad79c1933ce85c4aeca6a037cc4f77f Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:37:31 +0800
+Subject: [PATCH 105/116] riscv: configs: add visionfive2 defconfig to kernel
+ 6.6
+
+add visionfive2 defconfig to kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ .../configs/starfive_visionfive2_defconfig | 427 ++++++++++++++++++
+ 1 file changed, 427 insertions(+)
+ create mode 100644 arch/riscv/configs/starfive_visionfive2_defconfig
+
+--- /dev/null
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -0,0 +1,427 @@
++CONFIG_COMPILE_TEST=y
++# CONFIG_WERROR is not set
++CONFIG_DEFAULT_HOSTNAME="StarFive"
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_USELIB=y
++CONFIG_NO_HZ_IDLE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_BPF_SYSCALL=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_CGROUPS=y
++CONFIG_CGROUP_SCHED=y
++CONFIG_CFS_BANDWIDTH=y
++CONFIG_CGROUP_BPF=y
++CONFIG_NAMESPACES=y
++CONFIG_USER_NS=y
++CONFIG_CHECKPOINT_RESTORE=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_EXPERT=y
++CONFIG_PERF_EVENTS=y
++CONFIG_SOC_STARFIVE=y
++CONFIG_NONPORTABLE=y
++CONFIG_SMP=y
++CONFIG_HZ_100=y
++CONFIG_HIBERNATION=y
++CONFIG_PM_DEBUG=y
++CONFIG_PM_ADVANCED_DEBUG=y
++CONFIG_PM_TEST_SUSPEND=y
++CONFIG_CPU_IDLE=y
++CONFIG_RISCV_SBI_CPUIDLE=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
++CONFIG_CPUFREQ_DT=y
++# CONFIG_SECCOMP is not set
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_BINFMT_MISC=y
++CONFIG_PAGE_REPORTING=y
++CONFIG_CMA=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_IP_MULTICAST=y
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_IP_PNP=y
++CONFIG_IP_PNP_DHCP=y
++CONFIG_IP_PNP_BOOTP=y
++CONFIG_IP_PNP_RARP=y
++CONFIG_NETFILTER=y
++CONFIG_NETFILTER_NETLINK_ACCT=y
++CONFIG_NETFILTER_NETLINK_QUEUE=y
++CONFIG_NF_CONNTRACK=y
++CONFIG_NF_TABLES=y
++CONFIG_NFT_CT=y
++CONFIG_NFT_COMPAT=y
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
++CONFIG_NETFILTER_XT_MATCH_IPCOMP=y
++CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
++CONFIG_NETFILTER_XT_MATCH_MAC=y
++CONFIG_NETFILTER_XT_MATCH_MARK=y
++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
++CONFIG_NETFILTER_XT_MATCH_SOCKET=y
++CONFIG_NETFILTER_XT_MATCH_STATE=y
++CONFIG_NETFILTER_XT_MATCH_STRING=y
++CONFIG_NETFILTER_XT_MATCH_U32=y
++CONFIG_NF_TABLES_IPV4=y
++CONFIG_NFT_DUP_IPV4=y
++CONFIG_NFT_FIB_IPV4=y
++CONFIG_IP_NF_IPTABLES=y
++CONFIG_IP_NF_FILTER=y
++CONFIG_IP_NF_TARGET_REJECT=y
++CONFIG_IP_NF_NAT=y
++CONFIG_IP_NF_TARGET_MASQUERADE=y
++CONFIG_IP_NF_TARGET_NETMAP=y
++CONFIG_IP_NF_TARGET_REDIRECT=y
++CONFIG_NETLINK_DIAG=y
++CONFIG_CAN=y
++CONFIG_BT=y
++CONFIG_BT_RFCOMM=y
++CONFIG_BT_RFCOMM_TTY=y
++CONFIG_BT_BNEP=y
++CONFIG_BT_BNEP_MC_FILTER=y
++CONFIG_BT_BNEP_PROTO_FILTER=y
++CONFIG_BT_HCIBTUSB=m
++# CONFIG_BT_HCIBTUSB_BCM is not set
++CONFIG_CFG80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_NET_9P=y
++CONFIG_NET_9P_VIRTIO=y
++CONFIG_PCI=y
++# CONFIG_PCIEASPM is not set
++CONFIG_PCIE_STARFIVE_HOST=y
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_CFI=y
++CONFIG_MTD_CFI_ADV_OPTIONS=y
++CONFIG_MTD_SPI_NOR=y
++CONFIG_OF_CONFIGFS=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_VIRTIO_BLK=y
++CONFIG_BLK_DEV_NVME=y
++CONFIG_EEPROM_AT24=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_BLK_DEV_SR=y
++CONFIG_SCSI_VIRTIO=y
++CONFIG_ATA=y
++CONFIG_SATA_AHCI=y
++CONFIG_MD=y
++CONFIG_BLK_DEV_DM=m
++CONFIG_NETDEVICES=y
++CONFIG_VIRTIO_NET=y
++# CONFIG_NET_VENDOR_ALACRITECH is not set
++# CONFIG_NET_VENDOR_AMAZON is not set
++# CONFIG_NET_VENDOR_AQUANTIA is not set
++# CONFIG_NET_VENDOR_ARC is not set
++# CONFIG_NET_VENDOR_BROADCOM is not set
++# CONFIG_NET_VENDOR_CADENCE is not set
++# CONFIG_NET_VENDOR_CAVIUM is not set
++# CONFIG_NET_VENDOR_CORTINA is not set
++# CONFIG_NET_VENDOR_EZCHIP is not set
++# CONFIG_NET_VENDOR_GOOGLE is not set
++# CONFIG_NET_VENDOR_HUAWEI is not set
++# CONFIG_NET_VENDOR_INTEL is not set
++# CONFIG_NET_VENDOR_MARVELL is not set
++# CONFIG_NET_VENDOR_MELLANOX is not set
++# CONFIG_NET_VENDOR_MICREL is not set
++# CONFIG_NET_VENDOR_MICROCHIP is not set
++# CONFIG_NET_VENDOR_MICROSEMI is not set
++# CONFIG_NET_VENDOR_NI is not set
++# CONFIG_NET_VENDOR_NATSEMI is not set
++# CONFIG_NET_VENDOR_NETRONOME is not set
++# CONFIG_NET_VENDOR_PENSANDO is not set
++# CONFIG_NET_VENDOR_QUALCOMM is not set
++CONFIG_R8169=y
++# CONFIG_NET_VENDOR_RENESAS is not set
++# CONFIG_NET_VENDOR_ROCKER is not set
++# CONFIG_NET_VENDOR_SAMSUNG is not set
++# CONFIG_NET_VENDOR_SEEQ is not set
++# CONFIG_NET_VENDOR_SOLARFLARE is not set
++# CONFIG_NET_VENDOR_SOCIONEXT is not set
++CONFIG_STMMAC_ETH=y
++CONFIG_STMMAC_SELFTESTS=y
++CONFIG_DWMAC_DWC_QOS_ETH=y
++CONFIG_DWMAC_STARFIVE=y
++# CONFIG_NET_VENDOR_SYNOPSYS is not set
++# CONFIG_NET_VENDOR_VIA is not set
++# CONFIG_NET_VENDOR_WIZNET is not set
++# CONFIG_NET_VENDOR_XILINX is not set
++CONFIG_MARVELL_PHY=y
++CONFIG_MICREL_PHY=y
++CONFIG_MICROCHIP_PHY=y
++CONFIG_MOTORCOMM_PHY=y
++CONFIG_IPMS_CAN=y
++CONFIG_IWLWIFI=y
++CONFIG_IWLDVM=y
++CONFIG_IWLMVM=y
++CONFIG_HOSTAP=y
++# CONFIG_RTL_CARDS is not set
++CONFIG_USB_WIFI_ECR6600U=y
++CONFIG_AIC_WLAN_SUPPORT=y
++CONFIG_AIC8800_WLAN_SUPPORT=m
++CONFIG_AIC_LOADFW_SUPPORT=m
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_KEYBOARD is not set
++# CONFIG_INPUT_MOUSE is not set
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_GOODIX=y
++CONFIG_TOUCHSCREEN_TINKER_FT5406=y
++CONFIG_SERIO_LIBPS2=y
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_8250_NR_UARTS=6
++CONFIG_SERIAL_8250_RUNTIME_UARTS=6
++CONFIG_SERIAL_8250_EXTENDED=y
++CONFIG_SERIAL_8250_MANY_PORTS=y
++CONFIG_SERIAL_8250_DW=y
++CONFIG_SERIAL_OF_PLATFORM=y
++CONFIG_TTY_PRINTK=y
++CONFIG_VIRTIO_CONSOLE=y
++CONFIG_HW_RANDOM=y
++CONFIG_HW_RANDOM_JH7110=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_DESIGNWARE_PLATFORM=y
++CONFIG_SPI=y
++CONFIG_SPI_CADENCE_QUADSPI=y
++CONFIG_SPI_PL022=y
++CONFIG_SPI_SIFIVE=y
++CONFIG_SPI_SPIDEV=y
++# CONFIG_PTP_1588_CLOCK is not set
++CONFIG_GPIO_SYSFS=y
++CONFIG_POWER_RESET=y
++CONFIG_POWER_RESET_GPIO_RESTART=y
++CONFIG_POWER_RESET_SYSCON=y
++CONFIG_POWER_RESET_SYSCON_POWEROFF=y
++CONFIG_SENSORS_SFCTEMP=y
++CONFIG_THERMAL=y
++CONFIG_THERMAL_WRITABLE_TRIPS=y
++CONFIG_CPU_THERMAL=y
++CONFIG_DEVFREQ_THERMAL=y
++CONFIG_THERMAL_EMULATION=y
++# CONFIG_HISI_THERMAL is not set
++CONFIG_WATCHDOG=y
++CONFIG_WATCHDOG_SYSFS=y
++CONFIG_MFD_AXP20X_I2C=y
++CONFIG_REGULATOR=y
++CONFIG_REGULATOR_AXP20X=y
++CONFIG_REGULATOR_GPIO=y
++CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY=y
++# CONFIG_MEDIA_CEC_SUPPORT is not set
++CONFIG_MEDIA_SUPPORT=y
++CONFIG_MEDIA_USB_SUPPORT=y
++CONFIG_USB_VIDEO_CLASS=y
++CONFIG_V4L_PLATFORM_DRIVERS=y
++CONFIG_V4L_MEM2MEM_DRIVERS=y
++CONFIG_VIDEO_CADENCE_CSI2RX=y
++CONFIG_VIDEO_WAVE_VPU=m
++CONFIG_VIN_SENSOR_OV4689=y
++CONFIG_VIN_SENSOR_IMX219=y
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIDEO_IMX219=y
++CONFIG_VIDEO_IMX708=y
++CONFIG_DRM_PANEL_SIMPLE=y
++CONFIG_DRM_PANEL_JADARD_JD9365DA_H3=y
++CONFIG_DRM_TOSHIBA_TC358762=y
++CONFIG_DRM_VERISILICON=y
++CONFIG_STARFIVE_INNO_HDMI=y
++CONFIG_STARFIVE_DSI=y
++CONFIG_DRM_IMG_ROGUE=y
++CONFIG_DRM_LEGACY=y
++CONFIG_FB=y
++CONFIG_BACKLIGHT_CLASS_DEVICE=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_USB_AUDIO=y
++CONFIG_SND_SOC=y
++CONFIG_SND_DESIGNWARE_I2S=y
++CONFIG_SND_SOC_RZ=m
++CONFIG_SND_SOC_STARFIVE=y
++CONFIG_SND_SOC_JH7110_PWMDAC=y
++CONFIG_SND_SOC_JH7110_TDM=y
++CONFIG_SND_SOC_AC108=y
++CONFIG_SND_SOC_WM8960=y
++CONFIG_SND_SIMPLE_CARD=y
++CONFIG_UHID=y
++CONFIG_USB=y
++CONFIG_USB_OTG=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_RENESAS_USBHS=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_UAS=y
++CONFIG_USB_CDNS_SUPPORT=y
++CONFIG_USB_CDNS3=y
++CONFIG_USB_CDNS3_GADGET=y
++CONFIG_USB_CDNS3_HOST=y
++CONFIG_USB_CDNS3_STARFIVE=y
++CONFIG_USB_SERIAL=m
++CONFIG_USB_SERIAL_GENERIC=y
++CONFIG_USB_SERIAL_AIRCABLE=m
++CONFIG_USB_SERIAL_ARK3116=m
++CONFIG_USB_SERIAL_BELKIN=m
++CONFIG_USB_SERIAL_CH341=m
++CONFIG_USB_SERIAL_WHITEHEAT=m
++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
++CONFIG_USB_SERIAL_CP210X=m
++CONFIG_USB_SERIAL_CYPRESS_M8=m
++CONFIG_USB_SERIAL_EMPEG=m
++CONFIG_USB_SERIAL_FTDI_SIO=m
++CONFIG_USB_SERIAL_VISOR=m
++CONFIG_USB_SERIAL_IPAQ=m
++CONFIG_USB_SERIAL_IR=m
++CONFIG_USB_SERIAL_EDGEPORT=m
++CONFIG_USB_SERIAL_EDGEPORT_TI=m
++CONFIG_USB_SERIAL_F81232=m
++CONFIG_USB_SERIAL_GARMIN=m
++CONFIG_USB_SERIAL_IPW=m
++CONFIG_USB_SERIAL_IUU=m
++CONFIG_USB_SERIAL_KEYSPAN_PDA=m
++CONFIG_USB_SERIAL_KEYSPAN=m
++CONFIG_USB_SERIAL_KLSI=m
++CONFIG_USB_SERIAL_KOBIL_SCT=m
++CONFIG_USB_SERIAL_MCT_U232=m
++CONFIG_USB_SERIAL_METRO=m
++CONFIG_USB_SERIAL_MOS7720=m
++CONFIG_USB_SERIAL_MOS7840=m
++CONFIG_USB_SERIAL_NAVMAN=m
++CONFIG_USB_SERIAL_PL2303=m
++CONFIG_USB_SERIAL_OTI6858=m
++CONFIG_USB_SERIAL_QCAUX=m
++CONFIG_USB_SERIAL_QUALCOMM=m
++CONFIG_USB_SERIAL_SPCP8X5=m
++CONFIG_USB_SERIAL_SAFE=m
++CONFIG_USB_SERIAL_SIERRAWIRELESS=m
++CONFIG_USB_SERIAL_SYMBOL=m
++CONFIG_USB_SERIAL_TI=m
++CONFIG_USB_SERIAL_CYBERJACK=m
++CONFIG_USB_SERIAL_OMNINET=m
++CONFIG_USB_SERIAL_OPTICON=m
++CONFIG_USB_SERIAL_XSENS_MT=m
++CONFIG_USB_SERIAL_WISHBONE=m
++CONFIG_USB_SERIAL_SSU100=m
++CONFIG_USB_SERIAL_DEBUG=m
++CONFIG_USB_GADGET=y
++CONFIG_USB_RENESAS_USBHS_UDC=m
++CONFIG_USB_CONFIGFS=y
++CONFIG_USB_CONFIGFS_SERIAL=y
++CONFIG_USB_CONFIGFS_ACM=y
++CONFIG_USB_CONFIGFS_OBEX=y
++CONFIG_USB_CONFIGFS_NCM=y
++CONFIG_USB_CONFIGFS_ECM=y
++CONFIG_USB_CONFIGFS_ECM_SUBSET=y
++CONFIG_USB_CONFIGFS_RNDIS=y
++CONFIG_USB_CONFIGFS_EEM=y
++CONFIG_USB_CONFIGFS_MASS_STORAGE=y
++CONFIG_USB_CONFIGFS_F_FS=y
++CONFIG_MMC=y
++CONFIG_MMC_DEBUG=y
++CONFIG_MMC_SDHCI=y
++CONFIG_MMC_SDHCI_PLTFM=y
++CONFIG_MMC_SDHCI_OF_DWCMSHC=y
++CONFIG_MMC_SPI=y
++CONFIG_MMC_SDHI=y
++CONFIG_MMC_DW=y
++CONFIG_MMC_DW_STARFIVE=y
++CONFIG_NEW_LEDS=y
++CONFIG_LEDS_CLASS=y
++CONFIG_LEDS_GPIO=y
++CONFIG_LEDS_TRIGGER_HEARTBEAT=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_STARFIVE=y
++CONFIG_RTC_DRV_GOLDFISH=y
++CONFIG_DMADEVICES=y
++CONFIG_AMBA_PL08X=y
++CONFIG_DW_AXI_DMAC=y
++CONFIG_DMATEST=y
++# CONFIG_VIRTIO_MENU is not set
++# CONFIG_VHOST_MENU is not set
++CONFIG_GOLDFISH=y
++CONFIG_CLK_STARFIVE_JH7110_AON=y
++CONFIG_CLK_STARFIVE_JH7110_STG=y
++CONFIG_CLK_STARFIVE_JH7110_ISP=y
++CONFIG_CLK_STARFIVE_JH7110_VOUT=y
++CONFIG_MAILBOX=y
++CONFIG_STARFIVE_MBOX=m
++CONFIG_STARFIVE_MBOX_TEST=m
++# CONFIG_IOMMU_SUPPORT is not set
++CONFIG_RPMSG_CHAR=y
++CONFIG_RPMSG_VIRTIO=y
++CONFIG_SIFIVE_CCACHE=y
++CONFIG_PM_DEVFREQ=y
++CONFIG_IIO=y
++CONFIG_IIO_ST_ACCEL_3AXIS=y
++CONFIG_PWM=y
++CONFIG_PWM_OCORES=y
++CONFIG_PHY_STARFIVE_JH7110_PCIE=y
++CONFIG_PHY_STARFIVE_JH7110_USB=y
++CONFIG_PHY_M31_DPHY_RX0=y
++CONFIG_RAS=y
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_POSIX_ACL=y
++CONFIG_BTRFS_FS=m
++CONFIG_BTRFS_FS_POSIX_ACL=y
++CONFIG_AUTOFS_FS=y
++CONFIG_FUSE_FS=y
++CONFIG_CUSE=y
++CONFIG_VIRTIO_FS=y
++CONFIG_FSCACHE=y
++CONFIG_FSCACHE_STATS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_UTF8=y
++CONFIG_EXFAT_FS=y
++CONFIG_NTFS_FS=y
++CONFIG_NTFS_RW=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_POSIX_ACL=y
++CONFIG_HUGETLBFS=y
++CONFIG_JFFS2_FS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V4=y
++CONFIG_NFS_V4_1=y
++CONFIG_NFS_V4_2=y
++CONFIG_ROOT_NFS=y
++CONFIG_CIFS=m
++# CONFIG_CIFS_STATS2 is not set
++CONFIG_CIFS_UPCALL=y
++CONFIG_CIFS_XATTR=y
++CONFIG_CIFS_POSIX=y
++# CONFIG_CIFS_DEBUG is not set
++CONFIG_CIFS_DFS_UPCALL=y
++CONFIG_CIFS_FSCACHE=y
++CONFIG_SMB_SERVER=m
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ISO8859_1=y
++CONFIG_INIT_STACK_NONE=y
++CONFIG_CRYPTO_USER=y
++CONFIG_CRYPTO_TEST=m
++CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_CRYPTO_USER_API_SKCIPHER=y
++CONFIG_CRYPTO_USER_API_RNG=y
++CONFIG_CRYPTO_USER_API_AEAD=y
++CONFIG_CRYPTO_DEV_VIRTIO=y
++CONFIG_CRYPTO_DEV_JH7110=y
++CONFIG_DMA_CMA=y
++CONFIG_PRINTK_TIME=y
++CONFIG_DEBUG_FS=y
++CONFIG_SOFTLOCKUP_DETECTOR=y
++CONFIG_WQ_WATCHDOG=y
++CONFIG_DEBUG_TIMEKEEPING=y
++CONFIG_DEBUG_RT_MUTEXES=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_RWSEMS=y
++CONFIG_DEBUG_LIST=y
++CONFIG_DEBUG_PLIST=y
++CONFIG_DEBUG_SG=y
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_EQS_DEBUG=y
++# CONFIG_FTRACE is not set
++# CONFIG_RUNTIME_TESTING_MENU is not set
++CONFIG_MEMTEST=y
--- /dev/null
+From 51c1cccb202d741eeb1de57e0ecf8fcaa8f059e8 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:36:31 +0800
+Subject: [PATCH 106/116] riscv: dts: starfive: update dts to kernel 6.6
+
+update dts to kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile | 5 +-
+ .../jh7110-starfive-visionfive-2-ac108.dts | 64 +++
+ .../jh7110-starfive-visionfive-2-tdm.dts | 59 +++
+ .../jh7110-starfive-visionfive-2-wm8960.dts | 71 +++
+ .../jh7110-starfive-visionfive-2.dtsi | 437 +++++++++++++++++-
+ 5 files changed, 633 insertions(+), 3 deletions(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -10,7 +10,10 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-be
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb \
++ jh7110-starfive-visionfive-2-ac108.dtb \
++ jh7110-starfive-visionfive-2-tdm.dtb \
++ jh7110-starfive-visionfive-2-wm8960.dtb
+
+ subdir-y += evb-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
+@@ -0,0 +1,64 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++ /* i2s + ac108 */
++ sound0: snd-card0 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "Starfive-AC108-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "i2s";
++ bitclock-master = <&sndcodec1>;
++ frame-master = <&sndcodec1>;
++
++ widgets =
++ "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ routing =
++ "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++
++ cpu {
++ sound-dai = <&i2srx_mst>;
++ };
++
++ sndcodec1: codec {
++ sound-dai = <&ac108>;
++ clocks = <&ac108_mclk>;
++ clock-names = "mclk";
++ };
++ };
++ };
++};
++
++&i2c0 {
++ ac108: ac108@3b {
++ compatible = "x-power,ac108_0";
++ reg = <0x3b>;
++ #sound-dai-cells = <0>;
++ data-protocol = <0>;
++ };
++};
++
++&i2srx_mst {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+@@ -0,0 +1,59 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++ sound-tdm {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "Starfive-TDM-Sound-Card";
++ simple-audio-card,widgets = "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ simple-audio-card,routing = "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "dsp_a";
++ bitclock-master = <&dailink_master>;
++ frame-master = <&dailink_master>;
++
++ cpu {
++ sound-dai = <&tdm>;
++ };
++ dailink_master: codec {
++ sound-dai = <&wm8960>;
++ clocks = <&wm8960_mclk>;
++ };
++ };
++ };
++};
++
++&i2c0 {
++ wm8960: codec@1a {
++ compatible = "wlf,wm8960";
++ reg = <0x1a>;
++ wlf,shared-lrclk;
++ #sound-dai-cells = <0>;
++ };
++};
++
++&tdm {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+@@ -0,0 +1,71 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++ /* i2s + wm8960 */
++ sound-wm8960 {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "Starfive-WM8960-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ status = "okay";
++ format = "i2s";
++ bitclock-master = <&sndcodec1>;
++ frame-master = <&sndcodec1>;
++
++ widgets =
++ "Microphone", "Mic Jack",
++ "Line", "Line In",
++ "Line", "Line Out",
++ "Speaker", "Speaker",
++ "Headphone", "Headphone Jack";
++ routing =
++ "Headphone Jack", "HP_L",
++ "Headphone Jack", "HP_R",
++ "Speaker", "SPK_LP",
++ "Speaker", "SPK_LN",
++ "LINPUT1", "Mic Jack",
++ "LINPUT3", "Mic Jack",
++ "RINPUT1", "Mic Jack",
++ "RINPUT2", "Mic Jack";
++ cpu0 {
++ sound-dai = <&i2srx>;
++ };
++ cpu1 {
++ sound-dai = <&i2stx1>;
++ };
++
++ sndcodec1:codec {
++ sound-dai = <&wm8960>;
++ clocks = <&wm8960_mclk>;
++ clock-names = "mclk";
++ };
++ };
++ };
++};
++
++&i2c0 {
++ wm8960: codec@1a {
++ compatible = "wlf,wm8960";
++ reg = <0x1a>;
++ wlf,shared-lrclk;
++ #sound-dai-cells = <0>;
++ };
++};
++
++&i2srx {
++ status = "okay";
++};
++
++&i2stx1 {
++ status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -7,6 +7,7 @@
+ /dts-v1/;
+ #include "jh7110.dtsi"
+ #include "jh7110-pinfunc.h"
++#include <dt-bindings/leds/common.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ / {
+@@ -37,6 +38,44 @@
+ reg = <0x0 0x40000000 0x1 0x0>;
+ };
+
++ reserved-memory {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges;
++
++ linux,cma {
++ compatible = "shared-dma-pool";
++ reusable;
++ size = <0x0 0x20000000>;
++ alignment = <0x0 0x1000>;
++ alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
++ linux,cma-default;
++ };
++
++ e24_mem: e24@c0000000 {
++ reg = <0x0 0x6ce00000 0x0 0x1600000>;
++ };
++
++ xrp_reserved: xrpbuffer@f0000000 {
++ reg = <0x0 0x69c00000 0x0 0x01ffffff
++ 0x0 0x6bc00000 0x0 0x00001000
++ 0x0 0x6bc01000 0x0 0x00fff000
++ 0x0 0x6cc00000 0x0 0x00001000>;
++ };
++ };
++
++ leds {
++ compatible = "gpio-leds";
++
++ led-ack {
++ gpios = <&aongpio 3 GPIO_ACTIVE_HIGH>;
++ color = <LED_COLOR_ID_GREEN>;
++ function = LED_FUNCTION_HEARTBEAT;
++ linux,default-trigger = "heartbeat";
++ label = "ack";
++ };
++ };
++
+ gpio-restart {
+ compatible = "gpio-restart";
+ gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
+@@ -69,6 +108,48 @@
+ };
+ };
+ };
++
++ sound-hdmi {
++ compatible = "simple-audio-card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ format = "i2s";
++ bitclock-master = <&sndi2s0>;
++ frame-master = <&sndi2s0>;
++ mclk-fs = <256>;
++ status = "okay";
++
++ sndi2s0: cpu {
++ sound-dai = <&i2stx0>;
++ };
++
++ codec {
++ sound-dai = <&hdmi>;
++ };
++ };
++ };
++
++ ac108_mclk: ac108_mclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ clk_ext_camera: clk-ext-camera {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ wm8960_mclk: wm8960_mclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24576000>;
++ };
+ };
+
+ &dvp_clk {
+@@ -177,6 +258,42 @@
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+ status = "okay";
++
++ seeed_plane_i2c@45 {
++ compatible = "seeed_panel";
++ reg = <0x45>;
++
++ port {
++ panel_out0: endpoint {
++ remote-endpoint = <&dsi0_output>;
++ };
++ };
++ };
++
++ tinker_ft5406: tinker_ft5406@38 {
++ compatible = "tinker_ft5406";
++ reg = <0x38>;
++ };
++
++ touchscreen@14 {
++ compatible = "goodix,gt911";
++ reg = <0x14>;
++ irq-gpios = <&sysgpio 30 GPIO_ACTIVE_HIGH>;
++ reset-gpios = <&sysgpio 31 GPIO_ACTIVE_HIGH>;
++ };
++
++ panel_radxa@19 {
++ compatible ="starfive_jadard";
++ reg = <0x19>;
++ reset-gpio = <&sysgpio 23 0>;
++ enable-gpio = <&sysgpio 22 0>;
++
++ port {
++ panel_out1: endpoint {
++ remote-endpoint = <&dsi1_output>;
++ };
++ };
++ };
+ };
+
+ &i2c5 {
+@@ -195,11 +312,36 @@
+ #interrupt-cells = <1>;
+
+ regulators {
++ mipi_0p9: ALDO1 {
++ regulator-boot-on;
++ regulator-compatible = "aldo1";
++ regulator-name = "mipi_0p9";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ };
++
++ hdmi_0p9: ALDO5 {
++ regulator-boot-on;
++ regulator-compatible = "aldo5";
++ regulator-name = "hdmi_0p9";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ };
++
++ hdmi_1p8: ALDO3 {
++ regulator-boot-on;
++ regulator-compatible = "aldo3";
++ regulator-name = "hdmi_1p8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ };
++
+ vcc_3v3: dcdc1 {
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
++ regulator-compatible = "dcdc1";
+ regulator-name = "vcc_3v3";
+ };
+
+@@ -207,6 +349,7 @@
+ regulator-always-on;
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1540000>;
++ regulator-compatible = "dcdc2";
+ regulator-name = "vdd-cpu";
+ };
+
+@@ -215,6 +358,7 @@
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
++ regulator-compatible = "aldo4";
+ regulator-name = "emmc_vdd";
+ };
+ };
+@@ -229,12 +373,68 @@
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c6_pins>;
+ status = "okay";
++
++ imx219: imx219@10 {
++ compatible = "sony,imx219";
++ reg = <0x10>;
++ clocks = <&clk_ext_camera>;
++ clock-names = "xclk";
++ reset-gpio = <&sysgpio 18 0>;
++ rotation = <0>;
++ orientation = <1>; //CAMERA_ORIENTATION_BACK
++
++ port {
++ /* CSI2 bus endpoint */
++ imx219_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_imx219>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <4>;
++ data-lanes = <0 1>;
++ lane-polarities = <0 0 0>;
++ link-frequencies = /bits/ 64 <456000000>;
++ };
++ };
++ };
++
++ imx708: imx708@1a {
++ compatible = "sony,imx708";
++ reg = <0x1a>;
++ clocks = <&clk_ext_camera>;
++ reset-gpio = <&sysgpio 18 0>;
++
++ port {
++ imx708_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_imx708>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <450000000>;
++ };
++ };
++ };
++
++ ov4689: ov4689@36 {
++ compatible = "ovti,ov4689";
++ reg = <0x36>;
++ clocks = <&clk_ext_camera>;
++ clock-names = "xclk";
++ rotation = <180>;
++
++ port {
++ /* Parallel bus endpoint */
++ ov4689_to_csi2rx0: endpoint {
++ remote-endpoint = <&csi2rx0_from_ov4689>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ };
++ };
++ };
+ };
+
+ &i2srx {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2srx_pins>;
+- status = "okay";
++ status = "disabled";
+ };
+
+ &i2stx0 {
+@@ -246,7 +446,7 @@
+ &i2stx1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2stx1_pins>;
+- status = "okay";
++ status = "disabled";
+ };
+
+ &mmc0 {
+@@ -661,6 +861,40 @@
+ slew-rate = <0>;
+ };
+ };
++
++ hdmi_pins: hdmi-0 {
++ scl-pins {
++ pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
++ GPOEN_SYS_HDMI_DDC_SCL,
++ GPI_SYS_HDMI_DDC_SCL)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ sda-pins {
++ pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
++ GPOEN_SYS_HDMI_DDC_SDA,
++ GPI_SYS_HDMI_DDC_SDA)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ cec-pins {
++ pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
++ GPOEN_SYS_HDMI_CEC_SDA,
++ GPI_SYS_HDMI_CEC_SDA)>;
++ bias-pull-up;
++ input-enable;
++ };
++
++ hpd-pins {
++ pinmux = <GPIOMUX(15, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_HDMI_HPD)>;
++ bias-disable; /* external pull-up */
++ input-enable;
++ };
++ };
+ };
+
+ &uart0 {
+@@ -689,3 +923,202 @@
+ &U74_4 {
+ cpu-supply = <&vdd_cpu>;
+ };
++
++
++&display {
++ ports = <&dc_out_dpi0>;
++ status = "okay";
++};
++
++&dc8200 {
++ status = "okay";
++
++ dc_out: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ dc_out_dpi0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&hdmi_input0>;
++ };
++ dc_out_dpi1: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&hdmi_in_lcdc>;
++ };
++
++ dc_out_dpi2: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&mipi_in>;
++ };
++ };
++};
++
++&hdmi {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&hdmi_pins>;
++ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++
++ hdmi_in: port {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ hdmi_in_lcdc: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&dc_out_dpi1>;
++ };
++ };
++};
++
++&rgb_output {
++ status = "disabled";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ port@0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0>;
++ hdmi_input0:endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&dc_out_dpi0>;
++ };
++ };
++ };
++};
++
++&dsi_output {
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ mipi_in: endpoint {
++ remote-endpoint = <&dc_out_dpi2>;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ sf_dpi_output: endpoint {
++ remote-endpoint = <&dsi_in_port>;
++ };
++ };
++ };
++};
++
++&mipi_dsi {
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ dsi0_output: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&panel_out0>;
++ };
++
++ dsi1_output: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&panel_out1>;
++ };
++
++ };
++
++ port@1{
++ reg = <1>;
++ dsi_in_port: endpoint {
++ remote-endpoint = <&sf_dpi_output>;
++ };
++ };
++
++ };
++};
++
++&mipi_dphy {
++ status = "okay";
++};
++
++&co_process {
++ memory-region = <&e24_mem>;
++ status = "okay";
++};
++
++
++&mailbox_contrl0 {
++ status = "okay";
++};
++
++&mailbox_client0 {
++ status = "okay";
++};
++
++&vpu_dec {
++ status = "okay";
++};
++
++&vpu_enc {
++ status = "okay";
++};
++
++&jpu {
++ status = "okay";
++};
++
++&gpu {
++ status = "okay";
++};
++
++&vin_sysctl {
++ /* when use dvp open this pinctrl*/
++ status = "okay";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@1 {
++ reg = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ /* CSI2 bus endpoint */
++ csi2rx0_from_imx219: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&imx219_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <4>;
++ data-lanes = <0 1>;
++ lane-polarities = <0 0 0>;
++ status = "okay";
++ };
++
++ csi2rx0_from_imx708: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&imx708_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <4>;
++ data-lanes = <0 1>;
++ lane-polarities = <0 0 0>;
++ status = "okay";
++ };
++
++ csi2rx0_from_ov4689: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&ov4689_to_csi2rx0>;
++ bus-type = <4>; /* MIPI CSI-2 D-PHY */
++ clock-lanes = <4>;
++ data-lanes = <0 1>;
++ status = "okay";
++ };
++ };
++ };
++};
--- /dev/null
+From 17a781895079f8f9b2c089d13a1e4d9789e018c5 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 20 Mar 2024 17:36:14 +0800
+Subject: [PATCH 107/116] riscv: dts: starfive: evb-overlay: Support SPI
+ overlay
+
+Add new compatibles to support SPI overlay.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+@@ -9,6 +9,7 @@
+ fragment@0 {
+ target-path = "/soc/spi@10060000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -17,6 +18,7 @@
+ fragment@1 {
+ target-path = "/soc/spi@10070000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -25,6 +27,7 @@
+ fragment@2 {
+ target-path = "/soc/spi@10080000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -33,6 +36,7 @@
+ fragment@3 {
+ target-path = "/soc/spi@12070000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -41,6 +45,7 @@
+ fragment@4 {
+ target-path = "/soc/spi@12080000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -49,6 +54,7 @@
+ fragment@5 {
+ target-path = "/soc/spi@12090000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
+@@ -57,6 +63,7 @@
+ fragment@6 {
+ target-path = "/soc/spi@120a0000";
+ __overlay__ {
++ compatible = "starfive,jh7110-spi-pl022";
+ status = "okay";
+ };
+ };
--- /dev/null
+From 49b818cdd08fca7ff761277635c0e5ab659becb8 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Thu, 21 Mar 2024 16:53:51 +0800
+Subject: [PATCH 108/116] riscv: configs: visionfive2: Add standard partition
+ for hibernation
+
+Add CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation".
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/configs/starfive_visionfive2_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/riscv/configs/starfive_visionfive2_defconfig
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -24,6 +24,7 @@ CONFIG_NONPORTABLE=y
+ CONFIG_SMP=y
+ CONFIG_HZ_100=y
+ CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation"
+ CONFIG_PM_DEBUG=y
+ CONFIG_PM_ADVANCED_DEBUG=y
+ CONFIG_PM_TEST_SUSPEND=y
--- /dev/null
+From 146eb94a08d12b5831e1d30455469750f7c5f2a3 Mon Sep 17 00:00:00 2001
+From: "minda.chen" <minda.chen@starfivetech.com>
+Date: Tue, 18 Oct 2022 09:57:39 +0800
+Subject: [PATCH 109/116] usb:xhci:To improve performance,usb using lowmem for
+ bulk xfer.
+
+Generate an usb low memory pool for usb 3.0 host
+read/write transfer, default size is 8M.
+
+Signed-off-by: minda.chen <minda.chen@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-evb.dts | 1 +
+ drivers/usb/core/hcd.c | 4 +-
+ drivers/usb/host/xhci-mem.c | 64 +++++++++++++++++++++
+ drivers/usb/host/xhci-plat.c | 8 +++
+ drivers/usb/host/xhci-ring.c | 3 +-
+ drivers/usb/host/xhci.c | 57 +++++++++++++++++-
+ drivers/usb/host/xhci.h | 11 ++++
+ 7 files changed, 145 insertions(+), 3 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-evb.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts
+@@ -31,5 +31,6 @@
+ };
+
+ &usb0 {
++ xhci-lowmem-pool;
+ status = "okay";
+ };
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -1439,7 +1439,9 @@ int usb_hcd_map_urb_for_dma(struct usb_h
+ if (ret == 0)
+ urb->transfer_flags |= URB_MAP_LOCAL;
+ } else if (hcd_uses_dma(hcd)) {
+- if (urb->num_sgs) {
++ if (urb->transfer_flags & URB_MAP_LOCAL)
++ return ret;
++ else if (urb->num_sgs) {
+ int n;
+
+ /* We don't support sg for isoc transfers ! */
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -14,6 +14,7 @@
+ #include <linux/slab.h>
+ #include <linux/dmapool.h>
+ #include <linux/dma-mapping.h>
++#include <linux/genalloc.h>
+
+ #include "xhci.h"
+ #include "xhci-trace.h"
+@@ -1842,6 +1843,7 @@ xhci_free_interrupter(struct xhci_hcd *x
+ void xhci_mem_cleanup(struct xhci_hcd *xhci)
+ {
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
++ struct xhci_lowmem_pool *pool;
+ int i, j, num_ports;
+
+ cancel_delayed_work_sync(&xhci->cmd_timer);
+@@ -1887,6 +1889,13 @@ void xhci_mem_cleanup(struct xhci_hcd *x
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Freed medium stream array pool");
+
++ if (xhci->lowmem_pool.pool) {
++ pool = &xhci->lowmem_pool;
++ dma_free_coherent(dev, pool->size, (void *)pool->cached_base, pool->dma_addr);
++ gen_pool_destroy(pool->pool);
++ pool->pool = NULL;
++ }
++
+ if (xhci->dcbaa)
+ dma_free_coherent(dev, sizeof(*xhci->dcbaa),
+ xhci->dcbaa, xhci->dcbaa->dma);
+@@ -2297,6 +2306,55 @@ xhci_add_interrupter(struct xhci_hcd *xh
+ return 0;
+ }
+
++int xhci_setup_local_lowmem(struct xhci_hcd *xhci, size_t size)
++{
++ int err;
++ void *buffer;
++ dma_addr_t dma_addr;
++ struct usb_hcd *hcd = xhci_to_hcd(xhci);
++ struct xhci_lowmem_pool *pool = &xhci->lowmem_pool;
++
++ if (!pool->pool) {
++ /* minimal alloc one page */
++ pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev));
++ if (IS_ERR(pool->pool))
++ return PTR_ERR(pool->pool);
++ }
++
++ buffer = dma_alloc_coherent(hcd->self.sysdev, size, &dma_addr,
++ GFP_KERNEL | GFP_DMA32);
++
++ if (IS_ERR(buffer)) {
++ err = PTR_ERR(buffer);
++ goto destroy_pool;
++ }
++
++ /*
++ * Here we pass a dma_addr_t but the arg type is a phys_addr_t.
++ * It's not backed by system memory and thus there's no kernel mapping
++ * for it.
++ */
++ err = gen_pool_add_virt(pool->pool, (unsigned long)buffer,
++ dma_addr, size, dev_to_node(hcd->self.sysdev));
++ if (err < 0) {
++ dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
++ err);
++ dma_free_coherent(hcd->self.sysdev, size, buffer, dma_addr);
++ goto destroy_pool;
++ }
++
++ pool->cached_base = (u64)buffer;
++ pool->dma_addr = dma_addr;
++
++ return 0;
++
++destroy_pool:
++ gen_pool_destroy(pool->pool);
++ pool->pool = NULL;
++ return err;
++}
++EXPORT_SYMBOL_GPL(xhci_setup_local_lowmem);
++
+ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
+ {
+ dma_addr_t dma;
+@@ -2433,6 +2491,12 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+
+ xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
+
++ if (xhci->quirks & XHCI_LOCAL_BUFFER) {
++ if (xhci_setup_local_lowmem(xhci,
++ xhci->lowmem_pool.size))
++ goto fail;
++ }
++
+ /*
+ * XXX: Might need to set the Interrupter Moderation Register to
+ * something other than the default (~1ms minimum between interrupts).
+--- a/drivers/usb/host/xhci-plat.c
++++ b/drivers/usb/host/xhci-plat.c
+@@ -253,6 +253,14 @@ int xhci_plat_probe(struct platform_devi
+ if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
+ xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
+
++ if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) {
++ xhci->quirks |= XHCI_LOCAL_BUFFER;
++ if (device_property_read_u32(tmpdev, "lowmem-pool-size",
++ &xhci->lowmem_pool.size)) {
++ xhci->lowmem_pool.size = 8 << 20;
++ } else
++ xhci->lowmem_pool.size <<= 20;
++ }
+ device_property_read_u32(tmpdev, "imod-interval-ns",
+ &xhci->imod_interval);
+ }
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -3650,7 +3650,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
+
+ full_len = urb->transfer_buffer_length;
+ /* If we have scatter/gather list, we use it. */
+- if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
++ if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)
++ && !(urb->transfer_flags & URB_MAP_LOCAL)) {
+ num_sgs = urb->num_mapped_sgs;
+ sg = urb->sg;
+ addr = (u64) sg_dma_address(sg);
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -18,6 +18,8 @@
+ #include <linux/slab.h>
+ #include <linux/dmi.h>
+ #include <linux/dma-mapping.h>
++#include <linux/dma-map-ops.h>
++#include <linux/genalloc.h>
+
+ #include "xhci.h"
+ #include "xhci-trace.h"
+@@ -1285,6 +1287,55 @@ static void xhci_unmap_temp_buf(struct u
+ urb->transfer_buffer = NULL;
+ }
+
++static void xhci_map_urb_local(struct usb_hcd *hcd, struct urb *urb,
++ gfp_t mem_flags)
++{
++ void *buffer;
++ dma_addr_t dma_handle;
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;
++
++ if (lowmem_pool->pool
++ && (usb_endpoint_type(&urb->ep->desc) == USB_ENDPOINT_XFER_BULK)
++ && (urb->transfer_buffer_length > PAGE_SIZE)
++ && urb->num_sgs && urb->sg && (sg_phys(urb->sg) > 0xffffffff)) {
++ buffer = gen_pool_dma_alloc(lowmem_pool->pool,
++ urb->transfer_buffer_length, &dma_handle);
++ if (buffer) {
++ urb->transfer_dma = dma_handle;
++ urb->transfer_buffer = buffer;
++ urb->transfer_flags |= URB_MAP_LOCAL;
++ if (usb_urb_dir_out(urb))
++ sg_copy_to_buffer(urb->sg, urb->num_sgs,
++ (void *)buffer,
++ urb->transfer_buffer_length);
++ }
++ }
++
++}
++
++static void xhci_unmap_urb_local(struct usb_hcd *hcd, struct urb *urb)
++{
++ dma_addr_t dma_handle;
++ u64 cached_buffer;
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;
++
++ if (urb->transfer_flags & URB_MAP_LOCAL) {
++ dma_handle = urb->transfer_dma;
++ cached_buffer = lowmem_pool->cached_base +
++ ((u32)urb->transfer_dma & (lowmem_pool->size - 1));
++ if (usb_urb_dir_in(urb))
++ sg_copy_from_buffer(urb->sg, urb->num_sgs,
++ (void *)cached_buffer, urb->transfer_buffer_length);
++ gen_pool_free(lowmem_pool->pool, (unsigned long)urb->transfer_buffer,
++ urb->transfer_buffer_length);
++ urb->transfer_flags &= ~URB_MAP_LOCAL;
++ urb->transfer_buffer = NULL;
++ }
++}
++
++
+ /*
+ * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT),
+ * we'll copy the actual data into the TRB address register. This is limited to
+@@ -1305,9 +1356,11 @@ static int xhci_map_urb_for_dma(struct u
+ if (xhci_urb_temp_buffer_required(hcd, urb))
+ return xhci_map_temp_buffer(hcd, urb);
+ }
++ xhci_map_urb_local(hcd, urb, mem_flags);
+ return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+ }
+
++
+ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+ {
+ struct xhci_hcd *xhci;
+@@ -1320,8 +1373,10 @@ static void xhci_unmap_urb_for_dma(struc
+
+ if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf)
+ xhci_unmap_temp_buf(hcd, urb);
+- else
++ else {
++ xhci_unmap_urb_local(hcd, urb);
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
++ }
+ }
+
+ /**
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1763,6 +1763,13 @@ struct xhci_hub {
+ u8 min_rev;
+ };
+
++struct xhci_lowmem_pool {
++ struct gen_pool *pool;
++ u64 cached_base;
++ dma_addr_t dma_addr;
++ unsigned int size;
++};
++
+ /* There is one xhci_hcd structure per controller */
+ struct xhci_hcd {
+ struct usb_hcd *main_hcd;
+@@ -1914,6 +1921,8 @@ struct xhci_hcd {
+ #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
+ #define XHCI_ZHAOXIN_HOST BIT_ULL(46)
+
++#define XHCI_LOCAL_BUFFER BIT_ULL(63)
++
+ unsigned int num_active_eps;
+ unsigned int limit_active_eps;
+ struct xhci_port *hw_ports;
+@@ -1943,6 +1952,8 @@ struct xhci_hcd {
+ struct list_head regset_list;
+
+ void *dbc;
++ struct xhci_lowmem_pool lowmem_pool;
++
+ /* platform-specific data -- must come last */
+ unsigned long priv[] __aligned(sizeof(s64));
+ };
--- /dev/null
+From a170cb9936bb0b00d58aaea40984dbce1169fe42 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 3 Jul 2023 16:14:20 +0800
+Subject: [PATCH 110/116] usb: xhci: using dma_alloc_noncoherent to alloc low
+ memory pool
+
+For RISCV_NONCACHEHERENT is set, using dma_alloc_noncoherent
+to alloc cached large block low memory buffer. And set default
+size to 4M. (largest size of continuous memory can be supported)
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/usb/host/xhci-mem.c | 29 ++++++++++++++++-------------
+ drivers/usb/host/xhci-plat.c | 9 +++++----
+ 2 files changed, 21 insertions(+), 17 deletions(-)
+
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -1891,7 +1891,8 @@ void xhci_mem_cleanup(struct xhci_hcd *x
+
+ if (xhci->lowmem_pool.pool) {
+ pool = &xhci->lowmem_pool;
+- dma_free_coherent(dev, pool->size, (void *)pool->cached_base, pool->dma_addr);
++ dma_free_noncoherent(dev, pool->size, (void *)pool->cached_base,
++ pool->dma_addr, DMA_BIDIRECTIONAL);
+ gen_pool_destroy(pool->pool);
+ pool->pool = NULL;
+ }
+@@ -2317,15 +2318,15 @@ int xhci_setup_local_lowmem(struct xhci_
+ if (!pool->pool) {
+ /* minimal alloc one page */
+ pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev));
+- if (IS_ERR(pool->pool))
+- return PTR_ERR(pool->pool);
++ if (!pool->pool)
++ return -ENOMEM;
+ }
+
+- buffer = dma_alloc_coherent(hcd->self.sysdev, size, &dma_addr,
+- GFP_KERNEL | GFP_DMA32);
++ buffer = dma_alloc_noncoherent(hcd->self.sysdev, size, &dma_addr,
++ DMA_BIDIRECTIONAL, GFP_ATOMIC);
+
+- if (IS_ERR(buffer)) {
+- err = PTR_ERR(buffer);
++ if (!buffer) {
++ err = -ENOMEM;
+ goto destroy_pool;
+ }
+
+@@ -2335,11 +2336,11 @@ int xhci_setup_local_lowmem(struct xhci_
+ * for it.
+ */
+ err = gen_pool_add_virt(pool->pool, (unsigned long)buffer,
+- dma_addr, size, dev_to_node(hcd->self.sysdev));
++ dma_addr, size, dev_to_node(hcd->self.sysdev));
+ if (err < 0) {
+ dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
+ err);
+- dma_free_coherent(hcd->self.sysdev, size, buffer, dma_addr);
++ dma_free_noncoherent(hcd->self.sysdev, size, buffer, dma_addr, DMA_BIDIRECTIONAL);
+ goto destroy_pool;
+ }
+
+@@ -2362,7 +2363,7 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+ unsigned int val, val2;
+ u64 val_64;
+ u32 page_size, temp;
+- int i;
++ int i, ret;
+
+ INIT_LIST_HEAD(&xhci->cmd_list);
+
+@@ -2492,9 +2493,11 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+ xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
+
+ if (xhci->quirks & XHCI_LOCAL_BUFFER) {
+- if (xhci_setup_local_lowmem(xhci,
+- xhci->lowmem_pool.size))
+- goto fail;
++ ret = xhci_setup_local_lowmem(xhci, xhci->lowmem_pool.size);
++ if (ret) {
++ xhci->quirks &= ~XHCI_LOCAL_BUFFER;
++ xhci_warn(xhci, "WARN: Can't alloc lowmem pool\n");
++ }
+ }
+
+ /*
+--- a/drivers/usb/host/xhci-plat.c
++++ b/drivers/usb/host/xhci-plat.c
+@@ -255,10 +255,11 @@ int xhci_plat_probe(struct platform_devi
+
+ if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) {
+ xhci->quirks |= XHCI_LOCAL_BUFFER;
+- if (device_property_read_u32(tmpdev, "lowmem-pool-size",
+- &xhci->lowmem_pool.size)) {
+- xhci->lowmem_pool.size = 8 << 20;
+- } else
++ ret = device_property_read_u32(tmpdev, "lowmem-pool-size",
++ &xhci->lowmem_pool.size);
++ if (ret || xhci->lowmem_pool.size >= 4)
++ xhci->lowmem_pool.size = 4 << 20;
++ else
+ xhci->lowmem_pool.size <<= 20;
+ }
+ device_property_read_u32(tmpdev, "imod-interval-ns",
--- /dev/null
+From fc86405992c37b1898fd9b9bc077be673d774269 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 22 Mar 2024 09:54:28 +0800
+Subject: [PATCH 111/116] riscv: dts: starfive: Add vf2-overlay dtso subdir
+
+Create subdir vf2-overlay/ and add overlay .dtso for VF2 board.
+The code is ported from tag JH7110_VF2_6.1_v5.11.4
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile | 1 +
+ .../boot/dts/starfive/vf2-overlay/Makefile | 3 +
+ .../starfive/vf2-overlay/vf2-overlay-can.dtso | 23 ++++++
+ .../vf2-overlay/vf2-overlay-uart3-i2c.dtso | 75 +++++++++++++++++++
+ 4 files changed, 102 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -9,6 +9,7 @@ DTC_FLAGS_jh7110-evb := -@
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+
++subdir-y += vf2-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb \
+ jh7110-starfive-visionfive-2-ac108.dtb \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0
++dtb-$(CONFIG_ARCH_STARFIVE) += vf2-overlay-uart3-i2c.dtbo \
++ vf2-overlay-can.dtbo
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
+@@ -0,0 +1,23 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //can0
++ fragment@0 {
++ target-path = "/soc/can@130d0000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ //can1
++ fragment@1 {
++ target-path = "/soc/can@130e0000";
++ __overlay__ {
++ status = "okay";
++ };
++ };
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
+@@ -0,0 +1,75 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++ compatible = "starfive,jh7110";
++
++ //sysgpio
++ fragment@0 {
++ target-path = "/soc/pinctrl@13040000";
++ __overlay__ {
++ dt_uart3_pins: dt-uart3-0 {
++ tx-pins {
++ pinmux = <GPIOMUX(60, GPOUT_SYS_UART3_TX,
++ GPOEN_ENABLE,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <12>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++
++ rx-pins {
++ pinmux = <GPIOMUX(63, GPOUT_LOW,
++ GPOEN_DISABLE,
++ GPI_SYS_UART3_RX)>;
++ bias-pull-up;
++ drive-strength = <2>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++ };
++
++ dt_i2c1_pins: dt-i2c1-0 {
++ i2c-pins {
++ pinmux = <GPIOMUX(42, GPOUT_LOW,
++ GPOEN_SYS_I2C1_CLK,
++ GPI_SYS_I2C1_CLK)>,
++ <GPIOMUX(43, GPOUT_LOW,
++ GPOEN_SYS_I2C1_DATA,
++ GPI_SYS_I2C1_DATA)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++ };
++ };
++
++ //uart3
++ fragment@1 {
++ target-path = "/soc/serial@12000000";
++ __overlay__ {
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_uart3_pins>;
++ status = "okay";
++ };
++ };
++
++ //i2c1
++ fragment@2 {
++ target-path = "/soc/i2c@10040000";
++ __overlay__ {
++ clock-frequency = <100000>;
++ i2c-sda-hold-time-ns = <300>;
++ i2c-sda-falling-time-ns = <510>;
++ i2c-scl-falling-time-ns = <510>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&dt_i2c1_pins>;
++ status = "okay";
++ };
++ };
++};
--- /dev/null
+From 2b098da69f9497e725683caf67fbecf79202b7fc Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 22 Mar 2024 11:33:40 +0800
+Subject: [PATCH 112/116] riscv: dts: starfive: visionfive 2: Add aliases for
+ i2c* and uart3
+
+Fix the numbers of i2c and uart3.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -15,7 +15,10 @@
+ ethernet0 = &gmac0;
+ ethernet1 = &gmac1;
+ i2c0 = &i2c0;
++ i2c1 = &i2c1;
+ i2c2 = &i2c2;
++ i2c3 = &i2c3;
++ i2c4 = &i2c4;
+ i2c5 = &i2c5;
+ i2c6 = &i2c6;
+ mmc0 = &mmc0;
+@@ -23,6 +26,7 @@
+ pcie0 = &pcie0;
+ pcie1 = &pcie1;
+ serial0 = &uart0;
++ serial3 = &uart3;
+ };
+
+ chosen {
--- /dev/null
+From 18b70b6f5192ee5363515b57e1f213d0698224ed Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Tue, 28 Nov 2023 15:37:14 +0800
+Subject: [PATCH 113/116] driver: bluetooth: add aic8800 driver support
+
+add aic8800 driver that can support sco profile
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+---
+ .../configs/starfive_visionfive2_defconfig | 3 +-
+ drivers/bluetooth/Kconfig | 4 +
+ drivers/bluetooth/Makefile | 1 +
+ drivers/bluetooth/aic_btusb/Makefile | 80 +
+ drivers/bluetooth/aic_btusb/aic_btusb.c | 5031 +++++++++++++++++
+ drivers/bluetooth/aic_btusb/aic_btusb.h | 753 +++
+ .../aic_btusb/aic_btusb_external_featrue.c | 126 +
+ .../aic_btusb/aic_btusb_external_featrue.h | 3 +
+ 8 files changed, 5999 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/bluetooth/aic_btusb/Makefile
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb.c
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb.h
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
+
+--- a/arch/riscv/configs/starfive_visionfive2_defconfig
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -87,8 +87,7 @@ CONFIG_BT_RFCOMM_TTY=y
+ CONFIG_BT_BNEP=y
+ CONFIG_BT_BNEP_MC_FILTER=y
+ CONFIG_BT_BNEP_PROTO_FILTER=y
+-CONFIG_BT_HCIBTUSB=m
+-# CONFIG_BT_HCIBTUSB_BCM is not set
++CONFIG_BT_AICUSB=y
+ CONFIG_CFG80211=y
+ CONFIG_MAC80211=y
+ CONFIG_RFKILL=y
+--- a/drivers/bluetooth/Kconfig
++++ b/drivers/bluetooth/Kconfig
+@@ -478,5 +478,9 @@ config BT_NXPUART
+ Say Y here to compile support for NXP Bluetooth UART device into
+ the kernel, or say M here to compile as a module (btnxpuart).
+
++config BT_AICUSB
++ tristate "AIC8800 btusb driver"
++ help
++ AIC8800 usb dongle bluetooth support driver
+
+ endmenu
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -51,3 +51,4 @@ hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci
+ hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
+ hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
+ hci_uart-objs := $(hci_uart-y)
++obj-$(CONFIG_BT_AICUSB) += aic_btusb/
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/Makefile
+@@ -0,0 +1,80 @@
++MODULE_NAME = aic_btusb
++
++CONFIG_AIC8800_BTUSB_SUPPORT = m
++CONFIG_SUPPORT_VENDOR_APCF = n
++# Need to set fw path in BOARD_KERNEL_CMDLINE
++CONFIG_USE_FW_REQUEST = n
++
++ifeq ($(CONFIG_SUPPORT_VENDOR_APCF), y)
++obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o aic_btusb_external_featrue.o
++else
++obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o
++endif
++
++ccflags-$(CONFIG_SUPPORT_VENDOR_APCF) += -DCONFIG_SUPPORT_VENDOR_APCF
++#$(MODULE_NAME)-y := aic_btusb_ioctl.o\
++# aic_btusb.o \
++
++ccflags-$(CONFIG_USE_FW_REQUEST) += -DCONFIG_USE_FW_REQUEST
++
++
++# Platform support list
++CONFIG_PLATFORM_ROCKCHIP ?= n
++CONFIG_PLATFORM_ALLWINNER ?= n
++CONFIG_PLATFORM_AMLOGIC ?= n
++CONFIG_PLATFORM_UBUNTU ?= y
++CONFIG_PLATFORM_ING ?= n
++
++ifeq ($(CONFIG_PLATFORM_ING), y)
++ARCH := mips
++KDIR ?= /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/opensource/kernel
++CROSS_COMPILE := /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/resource/toolchain/gcc_472/mips-gcc472-glibc216-32bit/bin/mips-linux-gnu-
++endif
++
++ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
++ccflags-$(CONFIG_PLATFORM_ROCKCHIP) += -DCONFIG_PLATFORM_ROCKCHIP
++#KDIR := /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
++#ARCH ?= arm
++#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
++#KDIR := /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
++#ARCH ?= arm
++#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
++ARCH := arm64
++KDIR ?= /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/kernel
++CROSS_COMPILE := /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
++
++endif
++
++ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
++ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_PLATFORM_ALLWINNER
++KDIR := /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
++ARCH ?= arm64
++CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
++endif
++
++ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
++ ccflags-$(CONFIG_PLATFORM_AMLOGIC) += -DCONFIG_PLATFORM_AMLOGIC
++endif
++
++ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
++ccflags-$(CONFIG_PLATFORM_UBUNTU) += -DCONFIG_PLATFORM_UBUNTU
++endif
++
++all: modules
++modules:
++ make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
++
++install:
++ mkdir -p $(MODDESTDIR)
++ install -p -m 644 $(MODULE_NAME).ko $(MODDESTDIR)/
++ /sbin/depmod -a ${KVER}
++ echo $(MODULE_NAME) >> /etc/modules-load.d/aic_bt.conf
++
++uninstall:
++ rm -rfv $(MODDESTDIR)/$(MODULE_NAME).ko
++ /sbin/depmod -a ${KVER}
++ rm -f /etc/modules-load.d/aic_bt.conf
++
++clean:
++ rm -rf *.o *.ko *.o.* *.mod.* modules.* Module.* .a* .o* .*.o.* *.mod .tmp* .cache.mk .Module.symvers.cmd .modules.order.cmd
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb.c
+@@ -0,0 +1,5031 @@
++/*
++ *
++ * AicSemi Bluetooth USB driver
++ *
++ *
++ *
++ * 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
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/usb.h>
++
++#include <linux/ioctl.h>
++#include <linux/io.h>
++#include <linux/firmware.h>
++#include <linux/vmalloc.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/reboot.h>
++
++#include "aic_btusb.h"
++
++#ifdef CONFIG_USE_FW_REQUEST
++#include <linux/firmware.h>
++#endif
++
++#define AICBT_RELEASE_NAME "202012_ANDROID"
++#define VERSION "2.1.0"
++
++#define SUSPNED_DW_FW 0
++
++
++static spinlock_t queue_lock;
++static spinlock_t dlfw_lock;
++static volatile uint16_t dlfw_dis_state = 0;
++
++/* USB Device ID */
++#define USB_VENDOR_ID_AIC 0xA69C
++#define USB_PRODUCT_ID_AIC8801 0x8801
++#define USB_PRODUCT_ID_AIC8800DC 0x88dc
++#define USB_PRODUCT_ID_AIC8800D80 0x8d81
++
++enum AICWF_IC{
++ PRODUCT_ID_AIC8801 = 0,
++ PRODUCT_ID_AIC8800DC,
++ PRODUCT_ID_AIC8800DW,
++ PRODUCT_ID_AIC8800D80
++};
++
++u16 g_chipid = PRODUCT_ID_AIC8801;
++u8 chip_id = 0;
++u8 sub_chip_id = 0;
++
++struct btusb_data {
++ struct hci_dev *hdev;
++ struct usb_device *udev;
++ struct usb_interface *intf;
++ struct usb_interface *isoc;
++
++ spinlock_t lock;
++
++ unsigned long flags;
++
++ struct work_struct work;
++ struct work_struct waker;
++
++ struct usb_anchor tx_anchor;
++ struct usb_anchor intr_anchor;
++ struct usb_anchor bulk_anchor;
++ struct usb_anchor isoc_anchor;
++ struct usb_anchor deferred;
++ int tx_in_flight;
++ spinlock_t txlock;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++ spinlock_t rxlock;
++ struct sk_buff *evt_skb;
++ struct sk_buff *acl_skb;
++ struct sk_buff *sco_skb;
++#endif
++#endif
++
++ struct usb_endpoint_descriptor *intr_ep;
++ struct usb_endpoint_descriptor *bulk_tx_ep;
++ struct usb_endpoint_descriptor *bulk_rx_ep;
++ struct usb_endpoint_descriptor *isoc_tx_ep;
++ struct usb_endpoint_descriptor *isoc_rx_ep;
++
++ __u8 cmdreq_type;
++
++ unsigned int sco_num;
++ int isoc_altsetting;
++ int suspend_count;
++ uint16_t sco_handle;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++ int (*recv_bulk) (struct btusb_data * data, void *buffer, int count);
++#endif
++#endif
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++ struct early_suspend early_suspend;
++#else
++ struct notifier_block pm_notifier;
++ struct notifier_block reboot_notifier;
++#endif
++ firmware_info *fw_info;
++
++#ifdef CONFIG_SCO_OVER_HCI
++ AIC_sco_card_t *pSCOSnd;
++#endif
++};
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
++static bool reset_on_close = 0;
++#endif
++
++#ifdef CONFIG_SCO_OVER_HCI
++struct snd_sco_cap_timer {
++ struct timer_list cap_timer;
++ struct timer_list play_timer;
++ struct btusb_data snd_usb_data;
++ int snd_sco_length;
++};
++static struct snd_sco_cap_timer snd_cap_timer;
++#endif
++
++
++int bt_support = 0;
++module_param(bt_support, int, 0660);
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++int vendor_apcf_sent_done = 0;
++#endif
++
++static inline int check_set_dlfw_state_value(uint16_t change_value)
++{
++ spin_lock(&dlfw_lock);
++ if(!dlfw_dis_state) {
++ dlfw_dis_state = change_value;
++ }
++ spin_unlock(&dlfw_lock);
++ return dlfw_dis_state;
++}
++
++static inline void set_dlfw_state_value(uint16_t change_value)
++{
++ spin_lock(&dlfw_lock);
++ dlfw_dis_state = change_value;
++ spin_unlock(&dlfw_lock);
++}
++
++
++
++
++static void aic_free( struct btusb_data *data)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
++ kfree(data);
++#endif
++ return;
++}
++
++static struct btusb_data *aic_alloc(struct usb_interface *intf)
++{
++ struct btusb_data *data;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
++ data = kzalloc(sizeof(*data), GFP_KERNEL);
++#else
++ data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
++#endif
++ return data;
++}
++
++static void print_acl(struct sk_buff *skb, int direction)
++{
++#if PRINT_ACL_DATA
++ //uint wlength = skb->len;
++ u16 *handle = (u16 *)(skb->data);
++ u16 len = *(handle+1);
++ //u8 *acl_data = (u8 *)(skb->data);
++
++ AICBT_INFO("aic %s: direction %d, handle %04x, len %d",
++ __func__, direction, *handle, len);
++#endif
++}
++
++static void print_sco(struct sk_buff *skb, int direction)
++{
++#if PRINT_SCO_DATA
++ uint wlength = skb->len;
++ u16 *handle = (u16 *)(skb->data);
++ u8 len = *(u8 *)(handle+1);
++ //u8 *sco_data =(u8 *)(skb->data);
++
++ AICBT_INFO("aic %s: direction %d, handle %04x, len %d,wlength %d",
++ __func__, direction, *handle, len,wlength);
++#endif
++}
++
++static void print_error_command(struct sk_buff *skb)
++{
++ u16 *opcode = (u16*)(skb->data);
++ u8 *cmd_data = (u8*)(skb->data);
++ u8 len = *(cmd_data+2);
++
++ printk(" 0x%04x,len:%d,", *opcode, len);
++#if CONFIG_BLUEDROID
++ switch (*opcode) {
++ case HCI_OP_INQUIRY:
++ printk("HCI_OP_INQUIRY");
++ break;
++ case HCI_OP_INQUIRY_CANCEL:
++ printk("HCI_OP_INQUIRY_CANCEL");
++ break;
++ case HCI_OP_EXIT_PERIODIC_INQ:
++ printk("HCI_OP_EXIT_PERIODIC_INQ");
++ break;
++ case HCI_OP_CREATE_CONN:
++ printk("HCI_OP_CREATE_CONN");
++ break;
++ case HCI_OP_DISCONNECT:
++ printk("HCI_OP_DISCONNECT");
++ break;
++ case HCI_OP_CREATE_CONN_CANCEL:
++ printk("HCI_OP_CREATE_CONN_CANCEL");
++ break;
++ case HCI_OP_ACCEPT_CONN_REQ:
++ printk("HCI_OP_ACCEPT_CONN_REQ");
++ break;
++ case HCI_OP_REJECT_CONN_REQ:
++ printk("HCI_OP_REJECT_CONN_REQ");
++ break;
++ case HCI_OP_AUTH_REQUESTED:
++ printk("HCI_OP_AUTH_REQUESTED");
++ break;
++ case HCI_OP_SET_CONN_ENCRYPT:
++ printk("HCI_OP_SET_CONN_ENCRYPT");
++ break;
++ case HCI_OP_REMOTE_NAME_REQ:
++ printk("HCI_OP_REMOTE_NAME_REQ");
++ break;
++ case HCI_OP_READ_REMOTE_FEATURES:
++ printk("HCI_OP_READ_REMOTE_FEATURES");
++ break;
++ case HCI_OP_SNIFF_MODE:
++ printk("HCI_OP_SNIFF_MODE");
++ break;
++ case HCI_OP_EXIT_SNIFF_MODE:
++ printk("HCI_OP_EXIT_SNIFF_MODE");
++ break;
++ case HCI_OP_SWITCH_ROLE:
++ printk("HCI_OP_SWITCH_ROLE");
++ break;
++ case HCI_OP_SNIFF_SUBRATE:
++ printk("HCI_OP_SNIFF_SUBRATE");
++ break;
++ case HCI_OP_RESET:
++ printk("HCI_OP_RESET");
++ break;
++ case HCI_OP_Write_Extended_Inquiry_Response:
++ printk("HCI_Write_Extended_Inquiry_Response");
++ break;
++ case HCI_OP_Write_Simple_Pairing_Mode:
++ printk("HCI_OP_Write_Simple_Pairing_Mode");
++ break;
++ case HCI_OP_Read_Buffer_Size:
++ printk("HCI_OP_Read_Buffer_Size");
++ break;
++ case HCI_OP_Host_Buffer_Size:
++ printk("HCI_OP_Host_Buffer_Size");
++ break;
++ case HCI_OP_Read_Local_Version_Information:
++ printk("HCI_OP_Read_Local_Version_Information");
++ break;
++ case HCI_OP_Read_BD_ADDR:
++ printk("HCI_OP_Read_BD_ADDR");
++ break;
++ case HCI_OP_Read_Local_Supported_Commands:
++ printk("HCI_OP_Read_Local_Supported_Commands");
++ break;
++ case HCI_OP_Write_Scan_Enable:
++ printk("HCI_OP_Write_Scan_Enable");
++ break;
++ case HCI_OP_Write_Current_IAC_LAP:
++ printk("HCI_OP_Write_Current_IAC_LAP");
++ break;
++ case HCI_OP_Write_Inquiry_Scan_Activity:
++ printk("HCI_OP_Write_Inquiry_Scan_Activity");
++ break;
++ case HCI_OP_Write_Class_of_Device:
++ printk("HCI_OP_Write_Class_of_Device");
++ break;
++ case HCI_OP_LE_Rand:
++ printk("HCI_OP_LE_Rand");
++ break;
++ case HCI_OP_LE_Set_Random_Address:
++ printk("HCI_OP_LE_Set_Random_Address");
++ break;
++ case HCI_OP_LE_Set_Extended_Scan_Enable:
++ printk("HCI_OP_LE_Set_Extended_Scan_Enable");
++ break;
++ case HCI_OP_LE_Set_Extended_Scan_Parameters:
++ printk("HCI_OP_LE_Set_Extended_Scan_Parameters");
++ break;
++ case HCI_OP_Set_Event_Filter:
++ printk("HCI_OP_Set_Event_Filter");
++ break;
++ case HCI_OP_Write_Voice_Setting:
++ printk("HCI_OP_Write_Voice_Setting");
++ break;
++ case HCI_OP_Change_Local_Name:
++ printk("HCI_OP_Change_Local_Name");
++ break;
++ case HCI_OP_Read_Local_Name:
++ printk("HCI_OP_Read_Local_Name");
++ break;
++ case HCI_OP_Wirte_Page_Timeout:
++ printk("HCI_OP_Wirte_Page_Timeout");
++ break;
++ case HCI_OP_LE_Clear_Resolving_List:
++ printk("HCI_OP_LE_Clear_Resolving_List");
++ break;
++ case HCI_OP_LE_Set_Addres_Resolution_Enable_Command:
++ printk("HCI_OP_LE_Set_Addres_Resolution_Enable_Command");
++ break;
++ case HCI_OP_Write_Inquiry_mode:
++ printk("HCI_OP_Write_Inquiry_mode");
++ break;
++ case HCI_OP_Write_Page_Scan_Type:
++ printk("HCI_OP_Write_Page_Scan_Type");
++ break;
++ case HCI_OP_Write_Inquiry_Scan_Type:
++ printk("HCI_OP_Write_Inquiry_Scan_Type");
++ break;
++ case HCI_OP_Delete_Stored_Link_Key:
++ printk("HCI_OP_Delete_Stored_Link_Key");
++ break;
++ case HCI_OP_LE_Read_Local_Resolvable_Address:
++ printk("HCI_OP_LE_Read_Local_Resolvable_Address");
++ break;
++ case HCI_OP_LE_Extended_Create_Connection:
++ printk("HCI_OP_LE_Extended_Create_Connection");
++ break;
++ case HCI_OP_Read_Remote_Version_Information:
++ printk("HCI_OP_Read_Remote_Version_Information");
++ break;
++ case HCI_OP_LE_Start_Encryption:
++ printk("HCI_OP_LE_Start_Encryption");
++ break;
++ case HCI_OP_LE_Add_Device_to_Resolving_List:
++ printk("HCI_OP_LE_Add_Device_to_Resolving_List");
++ break;
++ case HCI_OP_LE_Set_Privacy_Mode:
++ printk("HCI_OP_LE_Set_Privacy_Mode");
++ break;
++ case HCI_OP_LE_Connection_Update:
++ printk("HCI_OP_LE_Connection_Update");
++ break;
++ default:
++ printk("UNKNOW_HCI_COMMAND");
++ break;
++ }
++#endif //CONFIG_BLUEDROID
++}
++
++static void print_command(struct sk_buff *skb)
++{
++#if PRINT_CMD_EVENT
++ print_error_command(skb);
++#endif
++}
++
++
++enum CODEC_TYPE{
++ CODEC_CVSD,
++ CODEC_MSBC,
++};
++
++static enum CODEC_TYPE codec_type = CODEC_CVSD;
++static void set_select_msbc(enum CODEC_TYPE type);
++static enum CODEC_TYPE check_select_msbc(void);
++
++
++#if CONFIG_BLUEDROID
++
++/* Global parameters for bt usb char driver */
++#define BT_CHAR_DEVICE_NAME "aicbt_dev"
++struct mutex btchr_mutex;
++static struct sk_buff_head btchr_readq;
++static wait_queue_head_t btchr_read_wait;
++static wait_queue_head_t bt_dlfw_wait;
++static int bt_char_dev_registered;
++static dev_t bt_devid; /* bt char device number */
++static struct cdev bt_char_dev; /* bt character device structure */
++static struct class *bt_char_class; /* device class for usb char driver */
++static int bt_reset = 0;
++
++/* HCI device & lock */
++DEFINE_RWLOCK(hci_dev_lock);
++struct hci_dev *ghdev = NULL;
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++static int bypass_event(struct sk_buff *skb)
++{
++ int ret = 0;
++ u8 *opcode = (u8*)(skb->data);
++ //u8 len = *(opcode+1);
++ u16 sub_opcpde;
++
++ switch(*opcode) {
++ case HCI_EV_CMD_COMPLETE:
++ sub_opcpde = ((u16)opcode[3]|(u16)(opcode[4])<<8);
++ if(sub_opcpde == 0xfd57){
++ if(vendor_apcf_sent_done){
++ vendor_apcf_sent_done--;
++ printk("apcf bypass\r\n");
++ ret = 1;
++ }
++ }
++ break;
++ default:
++ break;
++ }
++ return ret;
++}
++#endif//CONFIG_SUPPORT_VENDOR_APCF
++static void print_event(struct sk_buff *skb)
++{
++#if PRINT_CMD_EVENT
++ //uint wlength = skb->len;
++ //uint icount = 0;
++ u8 *opcode = (u8*)(skb->data);
++ //u8 len = *(opcode+1);
++
++ printk("aic %s ", __func__);
++ switch (*opcode) {
++ case HCI_EV_INQUIRY_COMPLETE:
++ printk("HCI_EV_INQUIRY_COMPLETE");
++ break;
++ case HCI_EV_INQUIRY_RESULT:
++ printk("HCI_EV_INQUIRY_RESULT");
++ break;
++ case HCI_EV_CONN_COMPLETE:
++ printk("HCI_EV_CONN_COMPLETE");
++ break;
++ case HCI_EV_CONN_REQUEST:
++ printk("HCI_EV_CONN_REQUEST");
++ break;
++ case HCI_EV_DISCONN_COMPLETE:
++ printk("HCI_EV_DISCONN_COMPLETE");
++ break;
++ case HCI_EV_AUTH_COMPLETE:
++ printk("HCI_EV_AUTH_COMPLETE");
++ break;
++ case HCI_EV_REMOTE_NAME:
++ printk("HCI_EV_REMOTE_NAME");
++ break;
++ case HCI_EV_ENCRYPT_CHANGE:
++ printk("HCI_EV_ENCRYPT_CHANGE");
++ break;
++ case HCI_EV_CHANGE_LINK_KEY_COMPLETE:
++ printk("HCI_EV_CHANGE_LINK_KEY_COMPLETE");
++ break;
++ case HCI_EV_REMOTE_FEATURES:
++ printk("HCI_EV_REMOTE_FEATURES");
++ break;
++ case HCI_EV_REMOTE_VERSION:
++ printk("HCI_EV_REMOTE_VERSION");
++ break;
++ case HCI_EV_QOS_SETUP_COMPLETE:
++ printk("HCI_EV_QOS_SETUP_COMPLETE");
++ break;
++ case HCI_EV_CMD_COMPLETE:
++ printk("HCI_EV_CMD_COMPLETE");
++ break;
++ case HCI_EV_CMD_STATUS:
++ printk("HCI_EV_CMD_STATUS");
++ break;
++ case HCI_EV_ROLE_CHANGE:
++ printk("HCI_EV_ROLE_CHANGE");
++ break;
++ case HCI_EV_NUM_COMP_PKTS:
++ printk("HCI_EV_NUM_COMP_PKTS");
++ break;
++ case HCI_EV_MODE_CHANGE:
++ printk("HCI_EV_MODE_CHANGE");
++ break;
++ case HCI_EV_PIN_CODE_REQ:
++ printk("HCI_EV_PIN_CODE_REQ");
++ break;
++ case HCI_EV_LINK_KEY_REQ:
++ printk("HCI_EV_LINK_KEY_REQ");
++ break;
++ case HCI_EV_LINK_KEY_NOTIFY:
++ printk("HCI_EV_LINK_KEY_NOTIFY");
++ break;
++ case HCI_EV_CLOCK_OFFSET:
++ printk("HCI_EV_CLOCK_OFFSET");
++ break;
++ case HCI_EV_PKT_TYPE_CHANGE:
++ printk("HCI_EV_PKT_TYPE_CHANGE");
++ break;
++ case HCI_EV_PSCAN_REP_MODE:
++ printk("HCI_EV_PSCAN_REP_MODE");
++ break;
++ case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
++ printk("HCI_EV_INQUIRY_RESULT_WITH_RSSI");
++ break;
++ case HCI_EV_REMOTE_EXT_FEATURES:
++ printk("HCI_EV_REMOTE_EXT_FEATURES");
++ break;
++ case HCI_EV_SYNC_CONN_COMPLETE:
++ printk("HCI_EV_SYNC_CONN_COMPLETE");
++ break;
++ case HCI_EV_SYNC_CONN_CHANGED:
++ printk("HCI_EV_SYNC_CONN_CHANGED");
++ break;
++ case HCI_EV_SNIFF_SUBRATE:
++ printk("HCI_EV_SNIFF_SUBRATE");
++ break;
++ case HCI_EV_EXTENDED_INQUIRY_RESULT:
++ printk("HCI_EV_EXTENDED_INQUIRY_RESULT");
++ break;
++ case HCI_EV_IO_CAPA_REQUEST:
++ printk("HCI_EV_IO_CAPA_REQUEST");
++ break;
++ case HCI_EV_SIMPLE_PAIR_COMPLETE:
++ printk("HCI_EV_SIMPLE_PAIR_COMPLETE");
++ break;
++ case HCI_EV_REMOTE_HOST_FEATURES:
++ printk("HCI_EV_REMOTE_HOST_FEATURES");
++ break;
++ default:
++ printk("unknow event");
++ break;
++ }
++ printk("\n");
++#if 0
++ printk("%02x,len:%d,", *opcode,len);
++ for (icount = 2; (icount < wlength) && (icount < 24); icount++)
++ printk("%02x ", *(opcode+icount));
++ printk("\n");
++#endif
++#endif
++}
++
++static inline ssize_t usb_put_user(struct sk_buff *skb,
++ char __user *buf, int count)
++{
++ char __user *ptr = buf;
++ int len = min_t(unsigned int, skb->len, count);
++
++ if (copy_to_user(ptr, skb->data, len))
++ return -EFAULT;
++
++ return len;
++}
++
++static struct sk_buff *aic_skb_queue[QUEUE_SIZE];
++static int aic_skb_queue_front = 0;
++static int aic_skb_queue_rear = 0;
++
++static void aic_enqueue(struct sk_buff *skb)
++{
++ spin_lock(&queue_lock);
++ if (aic_skb_queue_front == (aic_skb_queue_rear + 1) % QUEUE_SIZE) {
++ /*
++ * If queue is full, current solution is to drop
++ * the following entries.
++ */
++ AICBT_WARN("%s: Queue is full, entry will be dropped", __func__);
++ } else {
++ aic_skb_queue[aic_skb_queue_rear] = skb;
++
++ aic_skb_queue_rear++;
++ aic_skb_queue_rear %= QUEUE_SIZE;
++
++ }
++ spin_unlock(&queue_lock);
++}
++
++static struct sk_buff *aic_dequeue_try(unsigned int deq_len)
++{
++ struct sk_buff *skb;
++ struct sk_buff *skb_copy;
++
++ if (aic_skb_queue_front == aic_skb_queue_rear) {
++ AICBT_WARN("%s: Queue is empty", __func__);
++ return NULL;
++ }
++
++ skb = aic_skb_queue[aic_skb_queue_front];
++ if (deq_len >= skb->len) {
++
++ aic_skb_queue_front++;
++ aic_skb_queue_front %= QUEUE_SIZE;
++
++ /*
++ * Return skb addr to be dequeued, and the caller
++ * should free the skb eventually.
++ */
++ return skb;
++ } else {
++ skb_copy = pskb_copy(skb, GFP_ATOMIC);
++ skb_pull(skb, deq_len);
++ /* Return its copy to be freed */
++ return skb_copy;
++ }
++}
++
++static inline int is_queue_empty(void)
++{
++ return (aic_skb_queue_front == aic_skb_queue_rear) ? 1 : 0;
++}
++
++static void aic_clear_queue(void)
++{
++ struct sk_buff *skb;
++ spin_lock(&queue_lock);
++ while(!is_queue_empty()) {
++ skb = aic_skb_queue[aic_skb_queue_front];
++ aic_skb_queue[aic_skb_queue_front] = NULL;
++ aic_skb_queue_front++;
++ aic_skb_queue_front %= QUEUE_SIZE;
++ if (skb) {
++ kfree_skb(skb);
++ }
++ }
++ spin_unlock(&queue_lock);
++}
++
++/*
++ * AicSemi - Integrate from hci_core.c
++ */
++
++/* Get HCI device by index.
++ * Device is held on return. */
++static struct hci_dev *hci_dev_get(int index)
++{
++ if (index != 0)
++ return NULL;
++
++ return ghdev;
++}
++
++/* ---- HCI ioctl helpers ---- */
++static int hci_dev_open(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int ret = 0;
++
++ AICBT_DBG("%s: dev %d", __func__, dev);
++
++ hdev = hci_dev_get(dev);
++ if (!hdev) {
++ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++ return -ENODEV;
++ }
++
++ if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
++ ret = -ENODEV;
++ goto done;
++ }
++
++ if (test_bit(HCI_UP, &hdev->flags)) {
++ ret = -EALREADY;
++ goto done;
++ }
++
++done:
++ return ret;
++}
++
++static int hci_dev_do_close(struct hci_dev *hdev)
++{
++ if (hdev->flush)
++ hdev->flush(hdev);
++ /* After this point our queues are empty
++ * and no tasks are scheduled. */
++ hdev->close(hdev);
++ /* Clear flags */
++ hdev->flags = 0;
++ return 0;
++}
++
++static int hci_dev_close(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int err;
++ hdev = hci_dev_get(dev);
++ if (!hdev) {
++ AICBT_ERR("%s: failed to get hci dev[Null]", __func__);
++ return -ENODEV;
++ }
++
++ err = hci_dev_do_close(hdev);
++
++ return err;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++/* copy data from the URB buffer into the ALSA ring buffer */
++static bool aic_copy_capture_data_to_alsa(struct btusb_data *data, uint8_t* p_data, unsigned int frames)
++{
++ struct snd_pcm_runtime *runtime;
++ unsigned int frame_bytes, frames1;
++ u8 *dest;
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++
++ runtime = pSCOSnd->capture.substream->runtime;
++ frame_bytes = 2;
++
++ dest = runtime->dma_area + pSCOSnd->capture.buffer_pos * frame_bytes;
++ if (pSCOSnd->capture.buffer_pos + frames <= runtime->buffer_size) {
++ memcpy(dest, p_data, frames * frame_bytes);
++ } else {
++ /* wrap around at end of ring buffer */
++ frames1 = runtime->buffer_size - pSCOSnd->capture.buffer_pos;
++ memcpy(dest, p_data, frames1 * frame_bytes);
++ memcpy(runtime->dma_area,
++ p_data + frames1 * frame_bytes,
++ (frames - frames1) * frame_bytes);
++ }
++
++ pSCOSnd->capture.buffer_pos += frames;
++ if (pSCOSnd->capture.buffer_pos >= runtime->buffer_size) {
++ pSCOSnd->capture.buffer_pos -= runtime->buffer_size;
++ }
++
++ if((pSCOSnd->capture.buffer_pos%runtime->period_size) == 0) {
++ snd_pcm_period_elapsed(pSCOSnd->capture.substream);
++ }
++
++ return false;
++}
++
++
++static void hci_send_to_alsa_ringbuffer(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++ uint8_t* p_data;
++ int sco_length = skb->len - HCI_SCO_HDR_SIZE;
++ u16 *handle = (u16 *) (skb->data);
++ //u8 errflg = (u8)((*handle & 0x3000) >> 12);
++
++ pSCOSnd->usb_data->sco_handle = (*handle & 0x0fff);
++
++ AICBT_DBG("%s, %x, %x %x\n", __func__,pSCOSnd->usb_data->sco_handle, *handle, errflg);
++
++ if (!hdev) {
++ AICBT_INFO("%s: Frame for unknown HCI device", __func__);
++ return;
++ }
++
++ if (!test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
++ AICBT_INFO("%s: ALSA is not running", __func__);
++ return;
++ }
++ snd_cap_timer.snd_sco_length = sco_length;
++ p_data = (uint8_t *)skb->data + HCI_SCO_HDR_SIZE;
++ aic_copy_capture_data_to_alsa(data, p_data, sco_length/2);
++}
++
++#endif
++
++#if CONFIG_BLUEDROID
++static struct hci_dev *hci_alloc_dev(void)
++{
++ struct hci_dev *hdev;
++
++ hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
++ if (!hdev)
++ return NULL;
++
++ return hdev;
++}
++
++/* Free HCI device */
++static void hci_free_dev(struct hci_dev *hdev)
++{
++ kfree(hdev);
++}
++
++/* Register HCI device */
++static int hci_register_dev(struct hci_dev *hdev)
++{
++ int i, id;
++
++ AICBT_DBG("%s: %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
++ /* Do not allow HCI_AMP devices to register at index 0,
++ * so the index can be used as the AMP controller ID.
++ */
++ id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
++
++ write_lock(&hci_dev_lock);
++
++ sprintf(hdev->name, "hci%d", id);
++ hdev->id = id;
++ hdev->flags = 0;
++ hdev->dev_flags = 0;
++ mutex_init(&hdev->lock);
++
++ AICBT_DBG("%s: id %d, name %s", __func__, hdev->id, hdev->name);
++
++
++ for (i = 0; i < NUM_REASSEMBLY; i++)
++ hdev->reassembly[i] = NULL;
++
++ memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
++ atomic_set(&hdev->promisc, 0);
++
++ if (ghdev) {
++ write_unlock(&hci_dev_lock);
++ AICBT_ERR("%s: Hci device has been registered already", __func__);
++ return -1;
++ } else
++ ghdev = hdev;
++
++ write_unlock(&hci_dev_lock);
++
++ return id;
++}
++
++/* Unregister HCI device */
++static void hci_unregister_dev(struct hci_dev *hdev)
++{
++ int i;
++
++ AICBT_DBG("%s: hdev %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
++ set_bit(HCI_UNREGISTER, &hdev->dev_flags);
++
++ write_lock(&hci_dev_lock);
++ ghdev = NULL;
++ write_unlock(&hci_dev_lock);
++
++ hci_dev_do_close(hdev);
++ for (i = 0; i < NUM_REASSEMBLY; i++)
++ kfree_skb(hdev->reassembly[i]);
++}
++
++static void hci_send_to_stack(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ struct sk_buff *aic_skb_copy = NULL;
++
++ //AICBT_DBG("%s", __func__);
++
++ if (!hdev) {
++ AICBT_ERR("%s: Frame for unknown HCI device", __func__);
++ return;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++ AICBT_ERR("%s: HCI not running", __func__);
++ return;
++ }
++
++ aic_skb_copy = pskb_copy(skb, GFP_ATOMIC);
++ if (!aic_skb_copy) {
++ AICBT_ERR("%s: Copy skb error", __func__);
++ return;
++ }
++
++ memcpy(skb_push(aic_skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
++ aic_enqueue(aic_skb_copy);
++
++ /* Make sure bt char device existing before wakeup read queue */
++ hdev = hci_dev_get(0);
++ if (hdev) {
++ //AICBT_DBG("%s: Try to wakeup read queue", __func__);
++ AICBT_DBG("%s", __func__);
++ wake_up_interruptible(&btchr_read_wait);
++ }
++
++
++ return;
++}
++
++/* Receive frame from HCI drivers */
++static int hci_recv_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++ if (!hdev ||
++ (!test_bit(HCI_UP, &hdev->flags) && !test_bit(HCI_INIT, &hdev->flags))) {
++ kfree_skb(skb);
++ return -ENXIO;
++ }
++
++ /* Incomming skb */
++ bt_cb(skb)->incoming = 1;
++
++ /* Time stamp */
++ __net_timestamp(skb);
++
++ if (atomic_read(&hdev->promisc)) {
++#ifdef CONFIG_SCO_OVER_HCI
++ if(bt_cb(skb)->pkt_type == HCI_SCODATA_PKT){
++ hci_send_to_alsa_ringbuffer(hdev, skb);
++ }else{
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++ if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
++ if(bypass_event(skb)){
++ kfree_skb(skb);
++ return 0;
++ }
++ }
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++ hci_send_to_stack(hdev, skb);
++ }
++#else
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++ if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
++ if(bypass_event(skb)){
++ kfree_skb(skb);
++ return 0;
++ }
++ }
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++ /* Send copy to the sockets */
++ hci_send_to_stack(hdev, skb);
++#endif
++
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++
++
++static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
++ int count, __u8 index)
++{
++ int len = 0;
++ int hlen = 0;
++ int remain = count;
++ struct sk_buff *skb;
++ struct bt_skb_cb *scb;
++
++ //AICBT_DBG("%s", __func__);
++
++ if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
++ index >= NUM_REASSEMBLY)
++ return -EILSEQ;
++
++ skb = hdev->reassembly[index];
++
++ if (!skb) {
++ switch (type) {
++ case HCI_ACLDATA_PKT:
++ len = HCI_MAX_FRAME_SIZE;
++ hlen = HCI_ACL_HDR_SIZE;
++ break;
++ case HCI_EVENT_PKT:
++ len = HCI_MAX_EVENT_SIZE;
++ hlen = HCI_EVENT_HDR_SIZE;
++ break;
++ case HCI_SCODATA_PKT:
++ len = HCI_MAX_SCO_SIZE;
++ hlen = HCI_SCO_HDR_SIZE;
++ break;
++ }
++
++ skb = bt_skb_alloc(len, GFP_ATOMIC);
++ if (!skb)
++ return -ENOMEM;
++
++ scb = (void *) skb->cb;
++ scb->expect = hlen;
++ scb->pkt_type = type;
++
++ skb->dev = (void *) hdev;
++ hdev->reassembly[index] = skb;
++ }
++
++ while (count) {
++ scb = (void *) skb->cb;
++ len = min_t(uint, scb->expect, count);
++
++ memcpy(skb_put(skb, len), data, len);
++
++ count -= len;
++ data += len;
++ scb->expect -= len;
++ remain = count;
++
++ switch (type) {
++ case HCI_EVENT_PKT:
++ if (skb->len == HCI_EVENT_HDR_SIZE) {
++ struct hci_event_hdr *h = hci_event_hdr(skb);
++ scb->expect = h->plen;
++
++ if (skb_tailroom(skb) < scb->expect) {
++ kfree_skb(skb);
++ hdev->reassembly[index] = NULL;
++ return -ENOMEM;
++ }
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (skb->len == HCI_ACL_HDR_SIZE) {
++ struct hci_acl_hdr *h = hci_acl_hdr(skb);
++ scb->expect = __le16_to_cpu(h->dlen);
++
++ if (skb_tailroom(skb) < scb->expect) {
++ kfree_skb(skb);
++ hdev->reassembly[index] = NULL;
++ return -ENOMEM;
++ }
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (skb->len == HCI_SCO_HDR_SIZE) {
++ struct hci_sco_hdr *h = hci_sco_hdr(skb);
++ scb->expect = h->dlen;
++
++ if (skb_tailroom(skb) < scb->expect) {
++ kfree_skb(skb);
++ hdev->reassembly[index] = NULL;
++ return -ENOMEM;
++ }
++ }
++ break;
++ }
++
++ if (scb->expect == 0) {
++ /* Complete frame */
++ if(HCI_ACLDATA_PKT == type)
++ print_acl(skb,0);
++ if(HCI_SCODATA_PKT == type)
++ print_sco(skb,0);
++ if(HCI_EVENT_PKT == type)
++ print_event(skb);
++
++ bt_cb(skb)->pkt_type = type;
++ hci_recv_frame(skb);
++
++ hdev->reassembly[index] = NULL;
++ return remain;
++ }
++ }
++
++ return remain;
++}
++
++static int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
++{
++ int rem = 0;
++
++ if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT)
++ return -EILSEQ;
++
++ while (count) {
++ rem = hci_reassembly(hdev, type, data, count, type - 1);
++ if (rem < 0)
++ return rem;
++
++ data += (count - rem);
++ count = rem;
++ }
++
++ return rem;
++}
++#endif //CONFIG_BLUEDROID
++
++void hci_hardware_error(void)
++{
++ struct sk_buff *aic_skb_copy = NULL;
++ int len = 3;
++ uint8_t hardware_err_pkt[4] = {HCI_EVENT_PKT, 0x10, 0x01, HCI_VENDOR_USB_DISC_HARDWARE_ERROR};
++
++ aic_skb_copy = alloc_skb(len, GFP_ATOMIC);
++ if (!aic_skb_copy) {
++ AICBT_ERR("%s: Failed to allocate mem", __func__);
++ return;
++ }
++
++ memcpy(skb_put(aic_skb_copy, len), hardware_err_pkt, len);
++ aic_enqueue(aic_skb_copy);
++
++ wake_up_interruptible(&btchr_read_wait);
++}
++
++static int btchr_open(struct inode *inode_p, struct file *file_p)
++{
++ struct btusb_data *data;
++ struct hci_dev *hdev;
++
++ AICBT_DBG("%s: BT usb char device is opening", __func__);
++ /* Not open unless wanna tracing log */
++ /* trace_printk("%s: open....\n", __func__); */
++
++ hdev = hci_dev_get(0);
++ if (!hdev) {
++ AICBT_DBG("%s: Failed to get hci dev[NULL]", __func__);
++ return -ENODEV;
++ }
++ data = GET_DRV_DATA(hdev);
++
++ atomic_inc(&hdev->promisc);
++ /*
++ * As bt device is not re-opened when hotplugged out, we cannot
++ * trust on file's private data(may be null) when other file ops
++ * are invoked.
++ */
++ file_p->private_data = data;
++
++ mutex_lock(&btchr_mutex);
++ hci_dev_open(0);
++ mutex_unlock(&btchr_mutex);
++
++ aic_clear_queue();
++ return nonseekable_open(inode_p, file_p);
++}
++
++static int btchr_close(struct inode *inode_p, struct file *file_p)
++{
++ struct btusb_data *data;
++ struct hci_dev *hdev;
++
++ AICBT_INFO("%s: BT usb char device is closing", __func__);
++ /* Not open unless wanna tracing log */
++ /* trace_printk("%s: close....\n", __func__); */
++
++ data = file_p->private_data;
++ file_p->private_data = NULL;
++
++#if CONFIG_BLUEDROID
++ /*
++ * If the upper layer closes bt char interfaces, no reset
++ * action required even bt device hotplugged out.
++ */
++ bt_reset = 0;
++#endif
++
++ hdev = hci_dev_get(0);
++ if (hdev) {
++ atomic_set(&hdev->promisc, 0);
++ mutex_lock(&btchr_mutex);
++ hci_dev_close(0);
++ mutex_unlock(&btchr_mutex);
++ }
++
++ return 0;
++}
++
++static ssize_t btchr_read(struct file *file_p,
++ char __user *buf_p,
++ size_t count,
++ loff_t *pos_p)
++{
++ struct hci_dev *hdev;
++ struct sk_buff *skb;
++ ssize_t ret = 0;
++
++ while (count) {
++ hdev = hci_dev_get(0);
++ if (!hdev) {
++ /*
++ * Note: Only when BT device hotplugged out, we wil get
++ * into such situation. In order to keep the upper layer
++ * stack alive (blocking the read), we should never return
++ * EFAULT or break the loop.
++ */
++ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++ }
++
++ ret = wait_event_interruptible(btchr_read_wait, !is_queue_empty());
++ if (ret < 0) {
++ AICBT_ERR("%s: wait event is signaled %d", __func__, (int)ret);
++ break;
++ }
++
++ skb = aic_dequeue_try(count);
++ if (skb) {
++ ret = usb_put_user(skb, buf_p, count);
++ if (ret < 0)
++ AICBT_ERR("%s: Failed to put data to user space", __func__);
++ kfree_skb(skb);
++ break;
++ }
++ }
++
++ return ret;
++}
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++void btchr_external_write(char* buff, int len){
++ struct hci_dev *hdev;
++ struct sk_buff *skb;
++ int i;
++ struct btusb_data *data;
++
++ AICBT_INFO("%s \r\n", __func__);
++ for(i=0;i<len;i++){
++ printk("0x%x ",(u8)buff[i]);
++ }
++ printk("\r\n");
++ hdev = hci_dev_get(0);
++ if (!hdev) {
++ AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
++ return;
++ }
++ /* Never trust on btusb_data, as bt device may be hotplugged out */
++ data = GET_DRV_DATA(hdev);
++ if (!data) {
++ AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
++ return;
++ }
++ vendor_apcf_sent_done++;
++
++ skb = bt_skb_alloc(len, GFP_ATOMIC);
++ if (!skb)
++ return;
++ skb_reserve(skb, -1); // Add this line
++ skb->dev = (void *)hdev;
++ memcpy((__u8 *)skb->data,(__u8 *)buff,len);
++ skb_put(skb, len);
++ bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
++ skb_pull(skb, 1);
++ data->hdev->send(skb);
++}
++
++EXPORT_SYMBOL(btchr_external_write);
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++
++static ssize_t btchr_write(struct file *file_p,
++ const char __user *buf_p,
++ size_t count,
++ loff_t *pos_p)
++{
++ struct btusb_data *data = file_p->private_data;
++ struct hci_dev *hdev;
++ struct sk_buff *skb;
++
++ //AICBT_DBG("%s: BT usb char device is writing", __func__);
++ AICBT_DBG("%s", __func__);
++
++ hdev = hci_dev_get(0);
++ if (!hdev) {
++ AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
++ /*
++ * Note: we bypass the data from the upper layer if bt device
++ * is hotplugged out. Fortunatelly, H4 or H5 HCI stack does
++ * NOT check btchr_write's return value. However, returning
++ * count instead of EFAULT is preferable.
++ */
++ /* return -EFAULT; */
++ return count;
++ }
++
++ /* Never trust on btusb_data, as bt device may be hotplugged out */
++ data = GET_DRV_DATA(hdev);
++ if (!data) {
++ AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
++ return count;
++ }
++
++ if (count > HCI_MAX_FRAME_SIZE)
++ return -EINVAL;
++
++ skb = bt_skb_alloc(count, GFP_ATOMIC);
++ if (!skb)
++ return -ENOMEM;
++ skb_reserve(skb, -1); // Add this line
++
++ if (copy_from_user(skb_put(skb, count), buf_p, count)) {
++ AICBT_ERR("%s: Failed to get data from user space", __func__);
++ kfree_skb(skb);
++ return -EFAULT;
++ }
++
++ skb->dev = (void *)hdev;
++ bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
++ skb_pull(skb, 1);
++ data->hdev->send(skb);
++
++ return count;
++}
++
++static unsigned int btchr_poll(struct file *file_p, poll_table *wait)
++{
++ struct btusb_data *data = file_p->private_data;
++ struct hci_dev *hdev;
++
++ //AICBT_DBG("%s: BT usb char device is polling", __func__);
++
++ if(!bt_char_dev_registered) {
++ AICBT_ERR("%s: char device has not registered!", __func__);
++ return POLLERR | POLLHUP;
++ }
++
++ poll_wait(file_p, &btchr_read_wait, wait);
++
++ hdev = hci_dev_get(0);
++ if (!hdev) {
++ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++ mdelay(URB_CANCELING_DELAY_MS);
++ return POLLERR | POLLHUP;
++ return POLLOUT | POLLWRNORM;
++ }
++
++ /* Never trust on btusb_data, as bt device may be hotplugged out */
++ data = GET_DRV_DATA(hdev);
++ if (!data) {
++ /*
++ * When bt device is hotplugged out, btusb_data will
++ * be freed in disconnect.
++ */
++ AICBT_ERR("%s: Failed to get bt usb driver data[Null]", __func__);
++ mdelay(URB_CANCELING_DELAY_MS);
++ return POLLOUT | POLLWRNORM;
++ }
++
++ if (!is_queue_empty())
++ return POLLIN | POLLRDNORM;
++
++ return POLLOUT | POLLWRNORM;
++}
++static long btchr_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
++{
++ int ret = 0;
++ struct hci_dev *hdev;
++ struct btusb_data *data;
++ firmware_info *fw_info;
++
++ if(!bt_char_dev_registered) {
++ return -ENODEV;
++ }
++
++ if(check_set_dlfw_state_value(1) != 1) {
++ AICBT_ERR("%s bt controller is disconnecting!", __func__);
++ return 0;
++ }
++
++ hdev = hci_dev_get(0);
++ if(!hdev) {
++ AICBT_ERR("%s device is NULL!", __func__);
++ set_dlfw_state_value(0);
++ return 0;
++ }
++ data = GET_DRV_DATA(hdev);
++ fw_info = data->fw_info;
++
++ AICBT_INFO(" btchr_ioctl DOWN_FW_CFG with Cmd:%d",cmd);
++ switch (cmd) {
++ case DOWN_FW_CFG:
++ AICBT_INFO(" btchr_ioctl DOWN_FW_CFG");
++ ret = usb_autopm_get_interface(data->intf);
++ if (ret < 0){
++ goto failed;
++ }
++
++ //ret = download_patch(fw_info,1);
++ usb_autopm_put_interface(data->intf);
++ if(ret < 0){
++ AICBT_ERR("%s:Failed in download_patch with ret:%d",__func__,ret);
++ goto failed;
++ }
++
++ ret = hdev->open(hdev);
++ if(ret < 0){
++ AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
++ goto failed;
++ }
++ set_bit(HCI_UP, &hdev->flags);
++ set_dlfw_state_value(0);
++ wake_up_interruptible(&bt_dlfw_wait);
++ return 1;
++ case DWFW_CMPLT:
++ AICBT_INFO(" btchr_ioctl DWFW_CMPLT");
++#if 1
++ case SET_ISO_CFG:
++ AICBT_INFO("btchr_ioctl SET_ISO_CFG");
++ if(copy_from_user(&(hdev->voice_setting), (__u16*)arg, sizeof(__u16))){
++ AICBT_INFO(" voice settings err");
++ }
++ //hdev->voice_setting = *(uint16_t*)arg;
++ AICBT_INFO(" voice settings = %d", hdev->voice_setting);
++ //return 1;
++#endif
++ case GET_USB_INFO:
++ //ret = download_patch(fw_info,1);
++ AICBT_INFO(" btchr_ioctl GET_USB_INFO");
++ ret = hdev->open(hdev);
++ if(ret < 0){
++ AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
++ //goto done;
++ }
++ set_bit(HCI_UP, &hdev->flags);
++ set_dlfw_state_value(0);
++ wake_up_interruptible(&bt_dlfw_wait);
++ return 1;
++ case RESET_CONTROLLER:
++ AICBT_INFO(" btchr_ioctl RESET_CONTROLLER");
++ //reset_controller(fw_info);
++ return 1;
++ default:
++ AICBT_ERR("%s:Failed with wrong Cmd:%d",__func__,cmd);
++ goto failed;
++ }
++ failed:
++ set_dlfw_state_value(0);
++ wake_up_interruptible(&bt_dlfw_wait);
++ return ret;
++
++}
++
++#ifdef CONFIG_PLATFORM_UBUNTU//AIDEN
++typedef u32 compat_uptr_t;
++static inline void __user *compat_ptr(compat_uptr_t uptr)
++{
++ return (void __user *)(unsigned long)uptr;
++}
++#endif
++
++#ifdef CONFIG_COMPAT
++static long compat_btchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ AICBT_DBG("%s: enter",__func__);
++ return btchr_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
++}
++#endif
++static struct file_operations bt_chrdev_ops = {
++ open : btchr_open,
++ release : btchr_close,
++ read : btchr_read,
++ write : btchr_write,
++ poll : btchr_poll,
++ unlocked_ioctl : btchr_ioctl,
++#ifdef CONFIG_COMPAT
++ compat_ioctl : compat_btchr_ioctl,
++#endif
++};
++
++static int btchr_init(void)
++{
++ int res = 0;
++ struct device *dev;
++
++ AICBT_INFO("Register usb char device interface for BT driver");
++ /*
++ * btchr mutex is used to sync between
++ * 1) downloading patch and opening bt char driver
++ * 2) the file operations of bt char driver
++ */
++ mutex_init(&btchr_mutex);
++
++ skb_queue_head_init(&btchr_readq);
++ init_waitqueue_head(&btchr_read_wait);
++ init_waitqueue_head(&bt_dlfw_wait);
++
++ bt_char_class = class_create(THIS_MODULE, BT_CHAR_DEVICE_NAME);
++ if (IS_ERR(bt_char_class)) {
++ AICBT_ERR("Failed to create bt char class");
++ return PTR_ERR(bt_char_class);
++ }
++
++ res = alloc_chrdev_region(&bt_devid, 0, 1, BT_CHAR_DEVICE_NAME);
++ if (res < 0) {
++ AICBT_ERR("Failed to allocate bt char device");
++ goto err_alloc;
++ }
++
++ dev = device_create(bt_char_class, NULL, bt_devid, NULL, BT_CHAR_DEVICE_NAME);
++ if (IS_ERR(dev)) {
++ AICBT_ERR("Failed to create bt char device");
++ res = PTR_ERR(dev);
++ goto err_create;
++ }
++
++ cdev_init(&bt_char_dev, &bt_chrdev_ops);
++ res = cdev_add(&bt_char_dev, bt_devid, 1);
++ if (res < 0) {
++ AICBT_ERR("Failed to add bt char device");
++ goto err_add;
++ }
++
++ return 0;
++
++err_add:
++ device_destroy(bt_char_class, bt_devid);
++err_create:
++ unregister_chrdev_region(bt_devid, 1);
++err_alloc:
++ class_destroy(bt_char_class);
++ return res;
++}
++
++static void btchr_exit(void)
++{
++ AICBT_INFO("Unregister usb char device interface for BT driver");
++
++ device_destroy(bt_char_class, bt_devid);
++ cdev_del(&bt_char_dev);
++ unregister_chrdev_region(bt_devid, 1);
++ class_destroy(bt_char_class);
++
++ return;
++}
++#endif
++
++int send_hci_cmd(firmware_info *fw_info)
++{
++
++ int len = 0;
++ int ret_val = -1;
++ int i = 0;
++
++ if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
++ ret_val = usb_bulk_msg(fw_info->udev, fw_info->pipe_out, fw_info->send_pkt, fw_info->pkt_len,
++ &len, 3000);
++ if (ret_val || (len != fw_info->pkt_len)) {
++ AICBT_INFO("Error in send hci cmd = %d,"
++ "len = %d, size = %d", ret_val, len, fw_info->pkt_len);
++ }
++ }else if(g_chipid == PRODUCT_ID_AIC8800DC){
++ while((ret_val<0)&&(i++<3))
++ {
++ ret_val = usb_control_msg(
++ fw_info->udev, fw_info->pipe_out,
++ 0, USB_TYPE_CLASS, 0, 0,
++ (void *)(fw_info->send_pkt),
++ fw_info->pkt_len, MSG_TO);
++ }
++
++ }
++ return ret_val;
++
++}
++
++int rcv_hci_evt(firmware_info *fw_info)
++{
++ int ret_len = 0, ret_val = 0;
++ int i;
++
++ while (1) {
++ for(i = 0; i < 5; i++) {
++ ret_val = usb_interrupt_msg(
++ fw_info->udev, fw_info->pipe_in,
++ (void *)(fw_info->rcv_pkt), RCV_PKT_LEN,
++ &ret_len, MSG_TO);
++ if (ret_val >= 0)
++ break;
++ }
++
++ if (ret_val < 0)
++ return ret_val;
++
++ if (CMD_CMP_EVT == fw_info->evt_hdr->evt) {
++ if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode)
++ return ret_len;
++ }
++ }
++}
++
++int set_bt_onoff(firmware_info *fw_info, uint8_t onoff)
++{
++ int ret_val;
++
++ AICBT_INFO("%s: %s", __func__, onoff != 0 ? "on" : "off");
++
++ fw_info->cmd_hdr->opcode = cpu_to_le16(BTOFF_OPCODE);
++ fw_info->cmd_hdr->plen = 1;
++ fw_info->pkt_len = CMD_HDR_LEN + 1;
++ fw_info->send_pkt[CMD_HDR_LEN] = onoff;
++
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: Failed to send bt %s cmd, errno %d",
++ __func__, onoff != 0 ? "on" : "off", ret_val);
++ return ret_val;
++ }
++
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: Failed to receive bt %s event, errno %d",
++ __func__, onoff != 0 ? "on" : "off", ret_val);
++ return ret_val;
++ }
++
++ return ret_val;
++}
++
++//for 8800DC start
++u32 fwcfg_tbl[][2] = {
++ {0x40200028, 0x0021047e},
++ {0x40200024, 0x0000011d},
++};
++
++int fw_config(firmware_info* fw_info)
++{
++ int ret_val = -1;
++ struct hci_dbg_rd_mem_cmd *rd_cmd;
++ struct hci_dbg_rd_mem_cmd_evt *evt_para;
++ int len = 0, i = 0;
++ struct fw_status *evt_status;
++
++ rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
++ if (!rd_cmd)
++ return -ENOMEM;
++
++ rd_cmd->start_addr = 0x40200024;
++ rd_cmd->type = 32;
++ rd_cmd->length = 4;
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0) {
++ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++ __func__, fw_info->cmd_hdr->opcode, ret_val);
++ return ret_val;
++ }
++
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ printk("%s: Failed to receive hci event, errno %d",
++ __func__, ret_val);
++ return ret_val;
++ }
++
++ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++ __func__, evt_para->status, evt_para->length,
++ evt_para->data[0],
++ evt_para->data[1],
++ evt_para->data[2],
++ evt_para->data[3]);
++
++ ret_val = evt_para->status;
++ if (evt_para->status == 0) {
++ uint16_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8));
++ printk("%s rd_data is %x\n", __func__, rd_data);
++ if (rd_data == 0x119) {
++ struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
++ len = sizeof(fwcfg_tbl) / sizeof(u32) / 2;
++ patch_table_cmd->patch_num = len;
++ for (i = 0; i < len; i++) {
++ memcpy(&patch_table_cmd->patch_table_addr[i], &fwcfg_tbl[i][0], sizeof(uint32_t));
++ memcpy(&patch_table_cmd->patch_table_data[i], &fwcfg_tbl[i][1], sizeof(uint32_t));
++ printk("%s [%d] data: %08x %08x\n", __func__, i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
++ }
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
++ fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
++ fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++ return ret_val;
++ }
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ printk("%s: Failed to receive hci event, errno %d",
++ __func__, ret_val);
++ return ret_val;
++ }
++ evt_status = (struct fw_status *)fw_info->rsp_para;
++ ret_val = evt_status->status;
++ if (0 != evt_status->status) {
++ ret_val = -1;
++ } else {
++ ret_val = 0;
++ }
++
++ }
++ }
++ return ret_val;
++}
++
++int system_config(firmware_info *fw_info)
++{
++ int ret_val = -1;
++ struct hci_dbg_rd_mem_cmd *rd_cmd;
++ struct hci_dbg_rd_mem_cmd_evt *evt_para;
++ //int len = 0, i = 0;
++ //struct fw_status *evt_status;
++
++ rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
++ if (!rd_cmd)
++ return -ENOMEM;
++
++ rd_cmd->start_addr = 0x40500000;
++ rd_cmd->type = 32;
++ rd_cmd->length = 4;
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0)
++ {
++ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++ __func__, fw_info->cmd_hdr->opcode, ret_val);
++ return ret_val;
++ }
++
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0)
++ {
++ printk("%s: Failed to receive hci event, errno %d",
++ __func__, ret_val);
++ return ret_val;
++ }
++
++ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++ __func__, evt_para->status, evt_para->length,
++ evt_para->data[0],
++ evt_para->data[1],
++ evt_para->data[2],
++ evt_para->data[3]);
++
++ ret_val = evt_para->status;
++ if (evt_para->status == 0)
++ {
++ uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
++ //printk("%s 0x40500000 rd_data is %x\n", __func__, rd_data);
++ chip_id = (u8) (rd_data >> 16);
++ }
++
++ rd_cmd->start_addr = 0x20;
++ rd_cmd->type = 32;
++ rd_cmd->length = 4;
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0)
++ {
++ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++ __func__, fw_info->cmd_hdr->opcode, ret_val);
++ return ret_val;
++ }
++
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0)
++ {
++ printk("%s: Failed to receive hci event, errno %d",
++ __func__, ret_val);
++ return ret_val;
++ }
++
++ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++ __func__, evt_para->status, evt_para->length,
++ evt_para->data[0],
++ evt_para->data[1],
++ evt_para->data[2],
++ evt_para->data[3]);
++
++ ret_val = evt_para->status;
++ if (evt_para->status == 0)
++ {
++ uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
++ //printk("%s 0x02 rd_data is %x\n", __func__, rd_data);
++ sub_chip_id = (u8) (rd_data);
++ }
++ printk("chip_id = %x, sub_chip_id = %x\n", chip_id, sub_chip_id);
++ return ret_val;
++}
++
++int check_fw_status(firmware_info* fw_info)
++{
++ struct fw_status *read_ver_rsp;
++ int ret_val = -1;
++
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_FW_STATUS_GET_CMD);
++ fw_info->cmd_hdr->plen = 0;
++ fw_info->pkt_len = CMD_HDR_LEN;
++
++ ret_val = send_hci_cmd(fw_info);
++ if (ret_val < 0) {
++ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++ __func__, fw_info->cmd_hdr->opcode, ret_val);
++ return ret_val;
++ }
++
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ printk("%s: Failed to receive hci event, errno %d",
++ __func__, ret_val);
++ return ret_val;
++ }
++
++ read_ver_rsp = (struct fw_status *)(fw_info->rsp_para);
++
++ printk("%s: fw status = 0x%04x",
++ __func__, read_ver_rsp->status);
++ return read_ver_rsp->status;
++}
++
++int download_data(firmware_info *fw_info, u32 fw_addr, char *filename)
++{
++ unsigned int i=0;
++ int size;
++ u8 *dst=NULL;
++ int err=0;
++ struct hci_dbg_wr_mem_cmd *dl_cmd;
++ int hdr_len = sizeof(__le32) + sizeof(__u8) + sizeof(__u8);
++ int data_len = HCI_VSC_MEM_WR_SIZE;
++ int frag_len = data_len + hdr_len;
++ int ret_val;
++ int ncmd = 1;
++ struct fw_status *evt_para;
++
++ /* load aic firmware */
++ size = aic_load_firmware(&dst, filename, NULL);
++ if(size <= 0){
++ printk("wrong size of firmware file\n");
++ vfree(dst);
++ dst = NULL;
++ return -1;
++ }
++
++ dl_cmd = (struct hci_dbg_wr_mem_cmd *)(fw_info->req_para);
++ if (!dl_cmd)
++ return -ENOMEM;
++ evt_para = (struct fw_status *)fw_info->rsp_para;
++
++ /* Copy the file on the Embedded side */
++ printk("### Upload %s firmware, @ = %x size=%d\n", filename, fw_addr, size);
++
++ if (size > HCI_VSC_MEM_WR_SIZE) {// > 1KB data
++ for (i = 0; i < (size - HCI_VSC_MEM_WR_SIZE); i += HCI_VSC_MEM_WR_SIZE) {//each time write 240 bytes
++ data_len = HCI_VSC_MEM_WR_SIZE;
++ frag_len = data_len + hdr_len;
++ memcpy(dl_cmd->data, dst + i, data_len);
++ dl_cmd->length = data_len;
++ dl_cmd->type = 32;
++ dl_cmd->start_addr = fw_addr + i;
++ fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
++ fw_info->cmd_hdr->plen = frag_len;
++ fw_info->pkt_len = frag_len + 3;
++ #if 0
++ printk("[%d] data_len %d, src %x, dst %x\n", i, data_len, dst + i, fw_addr + i);
++ printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
++ print_hex_dump(KERN_ERR,"payload:",DUMP_PREFIX_NONE,16,1,dl_cmd->data,32,false);
++ /* Send download command */
++ print_hex_dump(KERN_ERR,"data:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
++ #endif
++ ret_val = send_hci_cmd(fw_info);
++
++ while (ncmd > 0) {
++ ret_val = rcv_hci_evt(fw_info);
++ printk("rcv_hci_evt %d\n", ret_val);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++ goto out;
++ } else {
++ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++ ncmd--;
++ }
++ if (0 != evt_para->status) {
++ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++ __func__, ret_val, evt_para->status);
++ ret_val = -1;
++ goto out;
++ } else {
++ ret_val = 0;
++ }
++ }
++ ncmd = 1;
++ }
++ }
++
++ if (!err && (i < size)) {// <1KB data
++ data_len = size - i;
++ frag_len = data_len + hdr_len;
++ memcpy(dl_cmd->data, dst + i, data_len);
++ dl_cmd->length = data_len;
++ dl_cmd->type = 32;
++ dl_cmd->start_addr = fw_addr + i;
++ fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
++ fw_info->cmd_hdr->plen = frag_len;
++ fw_info->pkt_len = frag_len + 3;
++ ret_val = send_hci_cmd(fw_info);
++ //printk("(%d) data_len %d, src %x, dst %x\n", i, data_len, (dst + i), fw_addr + i);
++ //printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
++ while (ncmd > 0) {
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++ goto out;
++ } else {
++ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++ ncmd--;
++ }
++ if (0 != evt_para->status) {
++ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++ __func__, ret_val, evt_para->status);
++ ret_val = -1;
++ goto out;
++ } else {
++ ret_val = 0;
++ }
++ }
++ ncmd = 0;
++ }
++
++out:
++ if (dst) {
++ vfree(dst);
++ dst = NULL;
++ }
++
++ printk("fw download complete\n\n");
++ return ret_val;
++
++}
++
++
++struct aicbt_info_t {
++ uint32_t btmode;
++ uint32_t btport;
++ uint32_t uart_baud;
++ uint32_t uart_flowctrl;
++ uint32_t lpm_enable;
++ uint32_t txpwr_lvl;
++};
++
++struct aicbsp_info_t {
++ int hwinfo;
++ uint32_t cpmode;
++};
++
++enum aicbsp_cpmode_type {
++ AICBSP_CPMODE_WORK,
++ AICBSP_CPMODE_TEST,
++};
++
++/* btmode
++ * used for force bt mode,if not AICBSP_MODE_NULL
++ * efuse valid and vendor_info will be invalid, even has beed set valid
++*/
++enum aicbt_btmode_type {
++ AICBT_BTMODE_BT_ONLY_SW = 0x0, // bt only mode with switch
++ AICBT_BTMODE_BT_WIFI_COMBO, // wifi/bt combo mode
++ AICBT_BTMODE_BT_ONLY, // bt only mode without switch
++ AICBT_BTMODE_BT_ONLY_TEST, // bt only test mode
++ AICBT_BTMODE_BT_WIFI_COMBO_TEST, // wifi/bt combo test mode
++ AICBT_MODE_NULL = 0xFF, // invalid value
++};
++
++enum aicbt_btport_type {
++ AICBT_BTPORT_NULL,
++ AICBT_BTPORT_MB,
++ AICBT_BTPORT_UART,
++};
++
++enum aicbt_uart_baud_type {
++ AICBT_UART_BAUD_115200 = 115200,
++ AICBT_UART_BAUD_921600 = 921600,
++ AICBT_UART_BAUD_1_5M = 1500000,
++ AICBT_UART_BAUD_3_25M = 3250000,
++};
++
++enum aicbt_uart_flowctrl_type {
++ AICBT_UART_FLOWCTRL_DISABLE = 0x0, // uart without flow ctrl
++ AICBT_UART_FLOWCTRL_ENABLE, // uart with flow ctrl
++};
++
++#define AICBSP_HWINFO_DEFAULT (-1)
++#define AICBSP_CPMODE_DEFAULT AICBSP_CPMODE_WORK
++#define AICBT_TXPWR_DFT 0x6F2F
++
++
++#define AICBT_BTMODE_DEFAULT AICBT_BTMODE_BT_WIFI_COMBO
++#define AICBT_BTPORT_DEFAULT AICBT_BTPORT_MB
++#define AICBT_UART_BAUD_DEFAULT AICBT_UART_BAUD_1_5M
++#define AICBT_UART_FC_DEFAULT AICBT_UART_FLOWCTRL_ENABLE
++#define AICBT_LPM_ENABLE_DEFAULT 0
++#define AICBT_TXPWR_LVL_DEFAULT AICBT_TXPWR_DFT
++
++struct aicbsp_info_t aicbsp_info = {
++ .hwinfo = AICBSP_HWINFO_DEFAULT,
++ .cpmode = AICBSP_CPMODE_DEFAULT,
++};
++
++#ifndef CONFIG_USE_FW_REQUEST
++#define FW_PATH_MAX 200
++
++char aic_fw_path[FW_PATH_MAX];
++#if (CONFIG_BLUEDROID == 0)
++static const char* aic_default_fw_path = "/lib/firmware/aic8800DC";
++#else
++static const char* aic_default_fw_path = "/vendor/etc/firmware";
++#endif
++#endif //CONFIG_USE_FW_REQUEST
++
++static struct aicbt_info_t aicbt_info = {
++ .btmode = AICBT_BTMODE_DEFAULT,
++ .btport = AICBT_BTPORT_DEFAULT,
++ .uart_baud = AICBT_UART_BAUD_DEFAULT,
++ .uart_flowctrl = AICBT_UART_FC_DEFAULT,
++ .lpm_enable = AICBT_LPM_ENABLE_DEFAULT,
++ .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT,
++};
++
++int patch_table_load(firmware_info *fw_info, struct aicbt_patch_table *_head)
++{
++ struct aicbt_patch_table *head, *p;
++ int i;
++ uint32_t *data = NULL;
++ struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
++ struct fw_status *evt_para;
++ int ret_val = 0;
++ int ncmd = 1;
++ uint32_t len = 0;
++ uint32_t tot_len = 0;
++ head = _head;
++ for (p = head; p != NULL; p = p->next) {
++ data = p->data;
++ if(AICBT_PT_BTMODE == p->type){
++ *(data + 1) = aicbsp_info.hwinfo < 0;
++ *(data + 3) = aicbsp_info.hwinfo;
++ *(data + 5) = aicbsp_info.cpmode;
++
++ *(data + 7) = aicbt_info.btmode;
++ *(data + 9) = aicbt_info.btport;
++ *(data + 11) = aicbt_info.uart_baud;
++ *(data + 13) = aicbt_info.uart_flowctrl;
++ *(data + 15) = aicbt_info.lpm_enable;
++ *(data + 17) = aicbt_info.txpwr_lvl;
++
++ }
++ if (p->type == AICBT_PT_NULL || p->type == AICBT_PT_PWRON) {
++ continue;
++ }
++ if (p->type == AICBT_PT_VER) {
++ char *data_s = (char *)p->data;
++ printk("patch version %s\n", data_s);
++ continue;
++ }
++ if (p->len == 0) {
++ printk("len is 0\n");
++ continue;
++ }
++ tot_len = p->len;
++ while (tot_len) {
++ if (tot_len > HCI_PT_MAX_LEN) {
++ len = HCI_PT_MAX_LEN;
++ } else {
++ len = tot_len;
++ }
++ for (i = 0; i < len; i++) {
++ patch_table_cmd->patch_num = len;
++ memcpy(&patch_table_cmd->patch_table_addr[i], data, sizeof(uint32_t));
++ memcpy(&patch_table_cmd->patch_table_data[i], data + 1, sizeof(uint32_t));
++ printk("[%d] data: %08x %08x\n", i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
++ data += 2;
++ }
++ tot_len -= len;
++ evt_para = (struct fw_status *)fw_info->rsp_para;
++ //print_hex_dump(KERN_ERR,"data0:",DUMP_PREFIX_NONE,16,1,patch_table_cmd,sizeof(struct aicbt_patch_table_cmd),false);
++
++ //printk("patch num %x %d\n", patch_table_cmd->patch_num, sizeof(struct aicbt_patch_table_cmd));
++ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
++ fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
++ fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
++ AICBT_DBG("patch num 0x%x, plen 0x%x\n", patch_table_cmd->patch_num, fw_info->cmd_hdr->plen );
++ //print_hex_dump(KERN_ERR,"patch table:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
++ ret_val = send_hci_cmd(fw_info);
++ while (ncmd > 0) {
++ ret_val = rcv_hci_evt(fw_info);
++ if (ret_val < 0) {
++ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++ goto out;
++ } else {
++ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++ ncmd--;
++ }
++ if (0 != evt_para->status) {
++ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++ __func__, ret_val, evt_para->status);
++ ret_val = -1;
++ goto out;
++ }
++ }
++ ncmd = 1;
++ }
++ }
++out:
++ aicbt_patch_table_free(&head);
++ return ret_val;
++}
++
++int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device)
++{
++
++#ifdef CONFIG_USE_FW_REQUEST
++ const struct firmware *fw = NULL;
++ u32 *dst = NULL;
++ void *buffer=NULL;
++ int size = 0;
++ int ret = 0;
++
++ printk("%s: request firmware = %s \n", __func__ ,name);
++
++
++ ret = request_firmware(&fw, name, NULL);
++
++ if (ret < 0) {
++ printk("Load %s fail\n", name);
++ release_firmware(fw);
++ return -1;
++ }
++
++ size = fw->size;
++ dst = (u32 *)fw->data;
++
++ if (size <= 0) {
++ printk("wrong size of firmware file\n");
++ release_firmware(fw);
++ return -1;
++ }
++
++
++ buffer = vmalloc(size);
++ memset(buffer, 0, size);
++ memcpy(buffer, dst, size);
++
++ *fw_buf = buffer;
++
++ release_firmware(fw);
++
++ return size;
++
++#else
++ u8 *buffer=NULL;
++ char *path=NULL;
++ struct file *fp=NULL;
++ int size = 0, len=0;
++ ssize_t rdlen=0;
++
++ /* get the firmware path */
++ path = __getname();
++ if (!path){
++ *fw_buf=NULL;
++ return -1;
++ }
++
++ if (strlen(aic_fw_path) > 0) {
++ printk("%s: use customer define fw_path\n", __func__);
++ len = snprintf(path, FW_PATH_MAX, "%s/%s", aic_fw_path, name);
++ } else {
++ len = snprintf(path, FW_PATH_MAX, "%s/%s",aic_default_fw_path, name);
++ }
++
++ if (len >= FW_PATH_MAX) {
++ printk("%s: %s file's path too long\n", __func__, name);
++ *fw_buf=NULL;
++ __putname(path);
++ return -1;
++ }
++
++ printk("%s :firmware path = %s \n", __func__ ,path);
++
++
++ /* open the firmware file */
++ fp=filp_open(path, O_RDONLY, 0);
++ if(IS_ERR(fp) || (!fp)){
++ printk("%s: %s file failed to open\n", __func__, name);
++ if(IS_ERR(fp))
++ printk("is_Err\n");
++ if((!fp))
++ printk("null\n");
++ *fw_buf=NULL;
++ __putname(path);
++ fp=NULL;
++ return -1;
++ }
++
++ size = i_size_read(file_inode(fp));
++ if(size<=0){
++ printk("%s: %s file size invalid %d\n", __func__, name, size);
++ *fw_buf=NULL;
++ __putname(path);
++ filp_close(fp,NULL);
++ fp=NULL;
++ return -1;
++}
++
++ /* start to read from firmware file */
++ buffer = vmalloc(size);
++ memset(buffer, 0, size);
++ if(!buffer){
++ *fw_buf=NULL;
++ __putname(path);
++ filp_close(fp,NULL);
++ fp=NULL;
++ return -1;
++ }
++
++
++ #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
++ rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
++ #else
++ rdlen = kernel_read(fp, fp->f_pos, buffer, size);
++ #endif
++
++ if(size != rdlen){
++ printk("%s: %s file rdlen invalid %d %d\n", __func__, name, (int)rdlen, size);
++ *fw_buf=NULL;
++ __putname(path);
++ filp_close(fp,NULL);
++ fp=NULL;
++ vfree(buffer);
++ buffer=NULL;
++ return -1;
++ }
++ if(rdlen > 0){
++ fp->f_pos += rdlen;
++ //printk("f_pos=%d\n", (int)fp->f_pos);
++ }
++ *fw_buf = buffer;
++
++#if 0
++ MD5Init(&md5);
++ MD5Update(&md5, (unsigned char *)dst, size);
++ MD5Final(&md5, decrypt);
++
++ printk(MD5PINRT, MD5(decrypt));
++
++#endif
++ return size;
++#endif
++}
++
++int aicbt_patch_table_free(struct aicbt_patch_table **head)
++{
++ struct aicbt_patch_table *p = *head, *n = NULL;
++ while (p) {
++ n = p->next;
++ kfree(p->name);
++ kfree(p->data);
++ kfree(p);
++ p = n;
++ }
++ *head = NULL;
++ return 0;
++}
++
++int get_patch_addr_from_patch_table(firmware_info *fw_info, char *filename, uint32_t *fw_patch_base_addr)
++{
++ int size;
++ int ret = 0;
++ uint8_t *rawdata=NULL;
++ uint8_t *p = NULL;
++ uint32_t *data = NULL;
++ uint32_t type = 0, len = 0;
++ int j;
++
++ /* load aic firmware */
++ size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
++
++ /* Copy the file on the Embedded side */
++ printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
++
++ if (size <= 0) {
++ printk("wrong size of firmware file\n");
++ ret = -1;
++ goto err;
++ }
++
++ p = rawdata;
++
++ if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
++ printk("TAG err\n");
++ ret = -1;
++ goto err;
++ }
++ p += 16;
++
++ while (p - rawdata < size) {
++ printk("size = %d p - rawdata = 0x%0lx \r\n", size, p - rawdata);
++ p += 16;
++
++ type = *(uint32_t *)p;
++ p += 4;
++
++ len = *(uint32_t *)p;
++ p += 4;
++ printk("cur->type %x, len %d\n", type, len);
++
++ if(type >= 1000 ) {//Temp Workaround
++ len = 0;
++ }else{
++ data = (uint32_t *)p;
++ if (type == AICBT_PT_NULL) {
++ *(fw_patch_base_addr) = *(data + 3);
++ printk("addr found %x\n", *(fw_patch_base_addr));
++ for (j = 0; j < len; j++) {
++ printk("addr %x\n", *(data+j));
++ }
++ break;
++ }
++ p += len * 8;
++ }
++ }
++
++ vfree(rawdata);
++ return ret;
++err:
++ //aicbt_patch_table_free(&head);
++
++ if (rawdata){
++ vfree(rawdata);
++ }
++ return ret;
++}
++
++
++
++int patch_table_download(firmware_info *fw_info, char *filename)
++{
++ struct aicbt_patch_table *head = NULL;
++ struct aicbt_patch_table *new = NULL;
++ struct aicbt_patch_table *cur = NULL;
++ int size;
++ int ret = 0;
++ uint8_t *rawdata=NULL;
++ uint8_t *p = NULL;
++
++ /* load aic firmware */
++ size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
++
++ /* Copy the file on the Embedded side */
++ printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
++
++ if (size <= 0) {
++ printk("wrong size of firmware file\n");
++ ret = -1;
++ goto err;
++ }
++
++ p = rawdata;
++
++ if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
++ printk("TAG err\n");
++ ret = -1;
++ goto err;
++ }
++ p += 16;
++
++ while (p - rawdata < size) {
++ printk("size = %d p - rawdata = 0x%0lx \r\n", size, p - rawdata);
++ new = (struct aicbt_patch_table *)kmalloc(sizeof(struct aicbt_patch_table), GFP_KERNEL);
++ memset(new, 0, sizeof(struct aicbt_patch_table));
++ if (head == NULL) {
++ head = new;
++ cur = new;
++ } else {
++ cur->next = new;
++ cur = cur->next;
++ }
++
++ cur->name = (char *)kmalloc(sizeof(char) * 16, GFP_KERNEL);
++ memset(cur->name, 0, sizeof(char) * 16);
++ memcpy(cur->name, p, 16);
++ p += 16;
++
++ cur->type = *(uint32_t *)p;
++ p += 4;
++
++ cur->len = *(uint32_t *)p;
++ p += 4;
++ printk("cur->type %x, len %d\n", cur->type, cur->len);
++
++ if((cur->type ) >= 1000 ) {//Temp Workaround
++ cur->len = 0;
++ }else{
++ cur->data = (uint32_t *)kmalloc(sizeof(uint8_t) * cur->len * 8, GFP_KERNEL);
++ memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8);
++ memcpy(cur->data, p, cur->len * 8);
++ p += cur->len * 8;
++ }
++ }
++
++ vfree(rawdata);
++ patch_table_load(fw_info, head);
++ printk("fw_patch_table download complete\n\n");
++
++ return ret;
++err:
++ //aicbt_patch_table_free(&head);
++
++ if (rawdata){
++ vfree(rawdata);
++ }
++ return ret;
++}
++
++
++int download_patch(firmware_info *fw_info, int cached)
++{
++ int ret_val = 0;
++
++ printk("%s: Download fw patch start, cached %d", __func__, cached);
++
++ if (!fw_info) {
++ printk("%s: No patch entry exists(fw_info %p)", __func__, fw_info);
++ ret_val = -1;
++ goto end;
++ }
++
++ ret_val = fw_config(fw_info);
++ if (ret_val) {
++ printk("%s: fw config failed %d", __func__, ret_val);
++ goto free;
++ }
++
++ ret_val = system_config(fw_info);
++ if (ret_val)
++ {
++ printk("%s: system config failed %d", __func__, ret_val);
++ goto free;
++ }
++
++ /*
++ * step1: check firmware statis
++ * step2: download firmware if updated
++ */
++
++
++ ret_val = check_fw_status(fw_info);
++
++
++ if (ret_val) {
++ #if 0
++ ret_val = download_data(fw_info, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME);
++ if (ret_val) {
++ printk("aic load adid fail %d\n", ret_val);
++ goto free;
++ }
++ #endif
++ if (sub_chip_id == 0) {
++ ret_val= download_data(fw_info, FW_RAM_PATCH_BASE_ADDR, FW_PATCH_BASE_NAME);
++ if (ret_val) {
++ printk("aic load patch fail %d\n", ret_val);
++ goto free;
++ }
++
++ ret_val= patch_table_download(fw_info, FW_PATCH_TABLE_NAME);
++ if (ret_val) {
++ printk("aic load patch ftable ail %d\n", ret_val);
++ goto free;
++ }
++ } else if (sub_chip_id == 1) {
++ uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
++
++ ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02, &fw_ram_patch_base_addr);
++ if (ret_val)
++ {
++ printk("aic get patch addr fail %d\n", ret_val);
++ goto free;
++ }
++ printk("%s %x\n", __func__, fw_ram_patch_base_addr);
++ ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02);
++ if (ret_val)
++ {
++ printk("aic load patch fail %d\n", ret_val);
++ goto free;
++ }
++
++ ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02);
++ if (ret_val)
++ {
++ printk("aic load patch ftable ail %d\n", ret_val);
++ goto free;
++ }
++ } else if (sub_chip_id == 2) {
++ uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
++
++ ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02H, &fw_ram_patch_base_addr);
++ if (ret_val)
++ {
++ printk("aic get patch addr fail %d\n", ret_val);
++ goto free;
++ }
++ printk("U02H %s %x\n", __func__, fw_ram_patch_base_addr);
++ ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02H);
++ if (ret_val)
++ {
++ printk("aic load patch fail %d\n", ret_val);
++ goto free;
++ }
++
++ ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02H);
++ if (ret_val)
++ {
++ printk("aic load patch ftable ail %d\n", ret_val);
++ goto free;
++ }
++ } else {
++ printk("%s unsupported sub_chip_id %x\n", __func__, sub_chip_id);
++ }
++ }
++
++free:
++ /* Free fw data after download finished */
++ kfree(fw_info->fw_data);
++ fw_info->fw_data = NULL;
++
++end:
++ return ret_val;
++}
++
++//for 8800dc end
++
++firmware_info *firmware_info_init(struct usb_interface *intf)
++{
++ struct usb_device *udev = interface_to_usbdev(intf);
++ firmware_info *fw_info;
++
++ AICBT_DBG("%s: start", __func__);
++
++ fw_info = kzalloc(sizeof(*fw_info), GFP_KERNEL);
++ if (!fw_info)
++ return NULL;
++
++ fw_info->send_pkt = kzalloc(SEND_PKT_LEN, GFP_KERNEL);
++ if (!fw_info->send_pkt) {
++ kfree(fw_info);
++ return NULL;
++ }
++
++ fw_info->rcv_pkt = kzalloc(RCV_PKT_LEN, GFP_KERNEL);
++ if (!fw_info->rcv_pkt) {
++ kfree(fw_info->send_pkt);
++ kfree(fw_info);
++ return NULL;
++ }
++
++ fw_info->intf = intf;
++ fw_info->udev = udev;
++if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
++ fw_info->pipe_in = usb_rcvbulkpipe(fw_info->udev, BULK_EP);
++ fw_info->pipe_out = usb_rcvbulkpipe(fw_info->udev, CTRL_EP);
++}else if(g_chipid == PRODUCT_ID_AIC8800DC){
++ fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP);
++ fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP);
++}
++ fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt);
++ fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt);
++ fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN);
++ fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN;
++ fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN;
++
++#if BTUSB_RPM
++ AICBT_INFO("%s: Auto suspend is enabled", __func__);
++ usb_enable_autosuspend(udev);
++ pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
++#else
++ AICBT_INFO("%s: Auto suspend is disabled", __func__);
++ usb_disable_autosuspend(udev);
++#endif
++
++#if BTUSB_WAKEUP_HOST
++ device_wakeup_enable(&udev->dev);
++#endif
++
++ return fw_info;
++}
++
++
++void firmware_info_destroy(struct usb_interface *intf)
++{
++ firmware_info *fw_info;
++ struct usb_device *udev;
++ struct btusb_data *data;
++
++ udev = interface_to_usbdev(intf);
++ data = usb_get_intfdata(intf);
++
++ fw_info = data->fw_info;
++ if (!fw_info)
++ return;
++
++#if BTUSB_RPM
++ usb_disable_autosuspend(udev);
++#endif
++
++ /*
++ * In order to reclaim fw data mem, we free fw_data immediately
++ * after download patch finished instead of here.
++ */
++ kfree(fw_info->rcv_pkt);
++ kfree(fw_info->send_pkt);
++ kfree(fw_info);
++
++
++}
++
++static struct usb_driver btusb_driver;
++
++static struct usb_device_id btusb_table[] = {
++ #if 0
++ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
++ USB_DEVICE_ID_MATCH_INT_INFO,
++ .idVendor = 0xa69d,
++ .bInterfaceClass = 0xe0,
++ .bInterfaceSubClass = 0x01,
++ .bInterfaceProtocol = 0x01 },
++ #endif
++ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8801, 0xe0, 0x01,0x01)},
++ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D80, 0xe0, 0x01,0x01)},
++ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800DC, 0xe0, 0x01,0x01)},
++ {}
++};
++
++MODULE_DEVICE_TABLE(usb, btusb_table);
++
++static int inc_tx(struct btusb_data *data)
++{
++ unsigned long flags;
++ int rv;
++
++ spin_lock_irqsave(&data->txlock, flags);
++ rv = test_bit(BTUSB_SUSPENDING, &data->flags);
++ if (!rv)
++ data->tx_in_flight++;
++ spin_unlock_irqrestore(&data->txlock, flags);
++
++ return rv;
++}
++
++void check_sco_event(struct urb *urb)
++{
++ u8* opcode = (u8*)(urb->transfer_buffer);
++ u8 status;
++ static uint16_t sco_handle = 0;
++ uint16_t handle;
++ u8 air_mode = 0;
++ struct hci_dev *hdev = urb->context;
++#ifdef CONFIG_SCO_OVER_HCI
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++#endif
++
++ switch (*opcode) {
++ case HCI_EV_SYNC_CONN_COMPLETE:
++ AICBT_INFO("%s: HCI_EV_SYNC_CONN_COMPLETE(0x%02x)", __func__, *opcode);
++ status = *(opcode + 2);
++ sco_handle = *(opcode + 3) | *(opcode + 4) << 8;
++ air_mode = *(opcode + 18);
++ printk("%s status:%d,air_mode:%d \r\n", __func__, status,air_mode);
++ if (status == 0) {
++ hdev->conn_hash.sco_num++;
++ hdev->notify(hdev, 0);
++ //schedule_work(&data->work);
++ if (air_mode == 0x03) {
++ set_select_msbc(CODEC_MSBC);
++ }
++ }
++ break;
++ case HCI_EV_DISCONN_COMPLETE:
++ AICBT_INFO("%s: HCI_EV_DISCONN_COMPLETE(0x%02x)", __func__, *opcode);
++ status = *(opcode + 2);
++ handle = *(opcode + 3) | *(opcode + 4) << 8;
++ if (status == 0 && sco_handle == handle) {
++ hdev->conn_hash.sco_num--;
++ hdev->notify(hdev, 0);
++ set_select_msbc(CODEC_CVSD);
++ //schedule_work(&data->work);
++#ifdef CONFIG_SCO_OVER_HCI
++ if (test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
++ mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
++ }
++#endif
++ }
++ break;
++ default:
++ AICBT_DBG("%s: event 0x%02x", __func__, *opcode);
++ break;
++ }
++}
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++static inline void btusb_free_frags(struct btusb_data *data)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&data->rxlock, flags);
++
++ kfree_skb(data->evt_skb);
++ data->evt_skb = NULL;
++
++ kfree_skb(data->acl_skb);
++ data->acl_skb = NULL;
++
++ kfree_skb(data->sco_skb);
++ data->sco_skb = NULL;
++
++ spin_unlock_irqrestore(&data->rxlock, flags);
++}
++
++static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
++{
++ struct sk_buff *skb;
++ int err = 0;
++
++ spin_lock(&data->rxlock);
++ skb = data->evt_skb;
++ //printk("%s count %d\n", __func__, count);
++
++#if 1
++ while (count) {
++ int len;
++
++ if (!skb) {
++ skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
++ if (!skb) {
++ err = -ENOMEM;
++ break;
++ }
++
++ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
++ bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
++ }
++
++ len = min_t(uint, bt_cb(skb)->expect, count);
++ memcpy(skb_put(skb, len), buffer, len);
++
++ count -= len;
++ buffer += len;
++ bt_cb(skb)->expect -= len;
++
++ if (skb->len == HCI_EVENT_HDR_SIZE) {
++ /* Complete event header */
++ bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
++
++ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++ kfree_skb(skb);
++ skb = NULL;
++
++ err = -EILSEQ;
++ break;
++ }
++ }
++
++ if (bt_cb(skb)->expect == 0) {
++ /* Complete frame */
++ hci_recv_frame(data->hdev, skb);
++ skb = NULL;
++ }
++ }
++#endif
++
++ data->evt_skb = skb;
++ spin_unlock(&data->rxlock);
++
++ return err;
++}
++
++static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
++{
++ struct sk_buff *skb;
++ int err = 0;
++
++ spin_lock(&data->rxlock);
++ skb = data->acl_skb;
++
++ while (count) {
++ int len;
++
++ if (!skb) {
++ skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
++ if (!skb) {
++ err = -ENOMEM;
++ break;
++ }
++
++ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
++ bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
++ }
++
++ len = min_t(uint, bt_cb(skb)->expect, count);
++ memcpy(skb_put(skb, len), buffer, len);
++
++ count -= len;
++ buffer += len;
++ bt_cb(skb)->expect -= len;
++
++ if (skb->len == HCI_ACL_HDR_SIZE) {
++ __le16 dlen = hci_acl_hdr(skb)->dlen;
++
++ /* Complete ACL header */
++ bt_cb(skb)->expect = __le16_to_cpu(dlen);
++
++ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++ kfree_skb(skb);
++ skb = NULL;
++
++ err = -EILSEQ;
++ break;
++ }
++ }
++
++ if (bt_cb(skb)->expect == 0) {
++ /* Complete frame */
++ hci_recv_frame(data->hdev, skb);
++ skb = NULL;
++ }
++ }
++
++ data->acl_skb = skb;
++ spin_unlock(&data->rxlock);
++
++ return err;
++}
++
++static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
++{
++ struct sk_buff *skb;
++ int err = 0;
++
++ spin_lock(&data->rxlock);
++ skb = data->sco_skb;
++
++ while (count) {
++ int len;
++
++ if (!skb) {
++ skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
++ if (!skb) {
++ err = -ENOMEM;
++ break;
++ }
++
++ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
++ bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
++ }
++
++ len = min_t(uint, bt_cb(skb)->expect, count);
++ memcpy(skb_put(skb, len), buffer, len);
++
++ count -= len;
++ buffer += len;
++ bt_cb(skb)->expect -= len;
++
++ if (skb->len == HCI_SCO_HDR_SIZE) {
++ /* Complete SCO header */
++ bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
++
++ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++ kfree_skb(skb);
++ skb = NULL;
++
++ err = -EILSEQ;
++ break;
++ }
++ }
++
++ if (bt_cb(skb)->expect == 0) {
++ /* Complete frame */
++ hci_recv_frame(data->hdev, skb);
++ skb = NULL;
++ }
++ }
++
++ data->sco_skb = skb;
++ spin_unlock(&data->rxlock);
++
++ return err;
++}
++#endif
++#endif // (CONFIG_BLUEDROID == 0)
++
++
++static void btusb_intr_complete(struct urb *urb)
++{
++ struct hci_dev *hdev = urb->context;
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ int err;
++
++ AICBT_DBG("%s: urb %p status %d count %d ", __func__,
++ urb, urb->status, urb->actual_length);
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++ printk("%s return \n", __func__);
++ return;
++ }
++ if (urb->status == 0) {
++ hdev->stat.byte_rx += urb->actual_length;
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++ if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
++ urb->transfer_buffer,
++ urb->actual_length) < 0) {
++ AICBT_ERR("%s: Corrupted event packet", __func__);
++ hdev->stat.err_rx++;
++ }
++#else
++ if (btusb_recv_intr(data, urb->transfer_buffer,
++ urb->actual_length) < 0) {
++ AICBT_ERR("%s corrupted event packet", hdev->name);
++ hdev->stat.err_rx++;
++ }
++#endif
++
++#ifdef CONFIG_SCO_OVER_HCI
++ check_sco_event(urb);
++#endif
++#ifdef CONFIG_USB_AIC_UART_SCO_DRIVER
++ check_sco_event(urb);
++#endif
++
++ }
++ /* Avoid suspend failed when usb_kill_urb */
++ else if(urb->status == -ENOENT) {
++ return;
++ }
++
++
++ if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
++ return;
++
++ usb_mark_last_busy(data->udev);
++ usb_anchor_urb(urb, &data->intr_anchor);
++
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ if (err != -EPERM && err != -ENODEV)
++ AICBT_ERR("%s: Failed to re-submit urb %p, err %d",
++ __func__, urb, err);
++ usb_unanchor_urb(urb);
++ }
++}
++
++static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ struct urb *urb;
++ unsigned char *buf;
++ unsigned int pipe;
++ int err, size;
++
++ if (!data->intr_ep)
++ return -ENODEV;
++
++ urb = usb_alloc_urb(0, mem_flags);
++ if (!urb)
++ return -ENOMEM;
++
++ size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
++
++ buf = kmalloc(size, mem_flags);
++ if (!buf) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++ __func__, size, data->intr_ep->bEndpointAddress);
++
++ pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
++
++ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
++ btusb_intr_complete, hdev,
++ data->intr_ep->bInterval);
++
++ urb->transfer_flags |= URB_FREE_BUFFER;
++
++ usb_anchor_urb(urb, &data->intr_anchor);
++
++ err = usb_submit_urb(urb, mem_flags);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, err %d",
++ __func__, urb, err);
++ usb_unanchor_urb(urb);
++ }
++
++ usb_free_urb(urb);
++
++ return err;
++}
++
++static void btusb_bulk_complete(struct urb *urb)
++{
++ struct hci_dev *hdev = urb->context;
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ int err;
++
++ AICBT_DBG("%s: urb %p status %d count %d",
++ __func__, urb, urb->status, urb->actual_length);
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++ printk("%s HCI_RUNNING\n", __func__);
++ return;
++ }
++ if (urb->status == 0) {
++ hdev->stat.byte_rx += urb->actual_length;
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++ if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
++ urb->transfer_buffer,
++ urb->actual_length) < 0) {
++ AICBT_ERR("%s: Corrupted ACL packet", __func__);
++ hdev->stat.err_rx++;
++ }
++#else
++ if (data->recv_bulk(data, urb->transfer_buffer,
++ urb->actual_length) < 0) {
++ AICBT_ERR("%s Corrupted ACL packet", hdev->name);
++ hdev->stat.err_rx++;
++ }
++#endif
++
++ }
++ /* Avoid suspend failed when usb_kill_urb */
++ else if(urb->status == -ENOENT) {
++ printk("%s ENOENT\n", __func__);
++ return;
++ }
++ AICBT_DBG("%s: OUT", __func__);
++
++ if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
++ printk("%s BTUSB_BULK_RUNNING\n", __func__);
++ return;
++ }
++ usb_anchor_urb(urb, &data->bulk_anchor);
++ usb_mark_last_busy(data->udev);
++
++ //printk("LIULI bulk submit\n");
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ /* -EPERM: urb is being killed;
++ * -ENODEV: device got disconnected */
++ if (err != -EPERM && err != -ENODEV)
++ AICBT_ERR("btusb_bulk_complete %s urb %p failed to resubmit (%d)",
++ hdev->name, urb, -err);
++ usb_unanchor_urb(urb);
++ }
++}
++
++static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ struct urb *urb;
++ unsigned char *buf;
++ unsigned int pipe;
++ int err, size = HCI_MAX_FRAME_SIZE;
++
++ AICBT_DBG("%s: hdev name %s", __func__, hdev->name);
++ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++ __func__, size, data->bulk_rx_ep->bEndpointAddress);
++
++ if (!data->bulk_rx_ep)
++ return -ENODEV;
++
++ urb = usb_alloc_urb(0, mem_flags);
++ if (!urb)
++ return -ENOMEM;
++
++ buf = kmalloc(size, mem_flags);
++ if (!buf) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
++
++ usb_fill_bulk_urb(urb, data->udev, pipe,
++ buf, size, btusb_bulk_complete, hdev);
++
++ urb->transfer_flags |= URB_FREE_BUFFER;
++
++ usb_mark_last_busy(data->udev);
++ usb_anchor_urb(urb, &data->bulk_anchor);
++
++ err = usb_submit_urb(urb, mem_flags);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
++ usb_unanchor_urb(urb);
++ }
++
++ usb_free_urb(urb);
++
++ return err;
++}
++
++static void btusb_isoc_complete(struct urb *urb)
++{
++ struct hci_dev *hdev = urb->context;
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ int i, err;
++ unsigned int total_length = 0;
++
++ AICBT_DBG("%s: urb %p status %d count %d",
++ __func__, urb, urb->status, urb->actual_length);
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return;
++
++ if (urb->status == 0) {
++ for (i = 0; i < urb->number_of_packets; i++) {
++ unsigned int offset = urb->iso_frame_desc[i].offset;
++ unsigned int length = urb->iso_frame_desc[i].actual_length;
++ //u8 *data = (u8 *)(urb->transfer_buffer + offset);
++ //AICBT_DBG("%d,%d ,%x,%x,%x s %d.",
++ //offset, length, data[0], data[1],data[2],urb->iso_frame_desc[i].status);
++
++ if(total_length >= urb->actual_length){
++ AICBT_ERR("total_len >= actual_length ,return");
++ break;
++ }
++ total_length += length;
++
++ if (urb->iso_frame_desc[i].status)
++ continue;
++
++ hdev->stat.byte_rx += length;
++ if(length){
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++ if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
++ urb->transfer_buffer + offset,
++ length) < 0) {
++ AICBT_ERR("%s: Corrupted SCO packet", __func__);
++ hdev->stat.err_rx++;
++ }
++#else
++ if (btusb_recv_isoc(data, urb->transfer_buffer + offset,
++ length) < 0) {
++ AICBT_ERR("%s corrupted SCO packet",
++ hdev->name);
++ hdev->stat.err_rx++;
++ }
++#endif
++
++ }
++ }
++ }
++ /* Avoid suspend failed when usb_kill_urb */
++ else if(urb->status == -ENOENT) {
++ return;
++ }
++
++
++ if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
++ return;
++
++ usb_anchor_urb(urb, &data->isoc_anchor);
++ i = 0;
++retry:
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ /* -EPERM: urb is being killed;
++ * -ENODEV: device got disconnected */
++ if (err != -EPERM && err != -ENODEV)
++ AICBT_ERR("%s: Failed to re-sumbit urb %p, retry %d, err %d",
++ __func__, urb, i, err);
++ if (i < 10) {
++ i++;
++ mdelay(1);
++ goto retry;
++ }
++
++ usb_unanchor_urb(urb);
++ }
++}
++
++static inline void fill_isoc_descriptor(struct urb *urb, int len, int mtu)
++{
++ int i, offset = 0;
++
++ AICBT_DBG("%s: len %d mtu %d", __func__, len, mtu);
++
++ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
++ i++, offset += mtu, len -= mtu) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = mtu;
++ }
++
++ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = len;
++ i++;
++ }
++
++ urb->number_of_packets = i;
++}
++
++static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ struct urb *urb;
++ unsigned char *buf;
++ unsigned int pipe;
++ int err, size;
++ int interval;
++
++ if (!data->isoc_rx_ep)
++ return -ENODEV;
++ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++ __func__, size, data->isoc_rx_ep->bEndpointAddress);
++
++ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
++ if (!urb)
++ return -ENOMEM;
++
++ size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
++ BTUSB_MAX_ISOC_FRAMES;
++
++ buf = kmalloc(size, mem_flags);
++ if (!buf) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
++
++ urb->dev = data->udev;
++ urb->pipe = pipe;
++ urb->context = hdev;
++ urb->complete = btusb_isoc_complete;
++ if (urb->dev->speed == USB_SPEED_HIGH || urb->dev->speed >= USB_SPEED_SUPER) {
++ /* make sure interval is within allowed range */
++ interval = clamp((int)data->isoc_rx_ep->bInterval, 1, 16);
++ urb->interval = 1 << (interval - 1);
++ } else {
++ urb->interval = data->isoc_rx_ep->bInterval;
++ }
++
++ AICBT_INFO("urb->interval %d \r\n", urb->interval);
++
++ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
++ urb->transfer_buffer = buf;
++ urb->transfer_buffer_length = size;
++
++ fill_isoc_descriptor(urb, size,
++ le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
++
++ usb_anchor_urb(urb, &data->isoc_anchor);
++
++ err = usb_submit_urb(urb, mem_flags);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
++ usb_unanchor_urb(urb);
++ }
++
++ usb_free_urb(urb);
++
++ return err;
++}
++
++static void btusb_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = urb->context;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto done;
++
++ if (!urb->status)
++ hdev->stat.byte_tx += urb->transfer_buffer_length;
++ else
++ hdev->stat.err_tx++;
++
++done:
++ spin_lock(&data->txlock);
++ data->tx_in_flight--;
++ spin_unlock(&data->txlock);
++
++ kfree(urb->setup_packet);
++
++ kfree_skb(skb);
++}
++
++static void btusb_isoc_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = urb->context;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++ AICBT_DBG("%s: urb %p status %d count %d",
++ __func__, urb, urb->status, urb->actual_length);
++
++ if (skb && hdev) {
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto done;
++
++ if (!urb->status)
++ hdev->stat.byte_tx += urb->transfer_buffer_length;
++ else
++ hdev->stat.err_tx++;
++ } else
++ AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
++
++done:
++ kfree(urb->setup_packet);
++
++ kfree_skb(skb);
++}
++
++#if (CONFIG_BLUEDROID == 0)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
++static int btusb_shutdown(struct hci_dev *hdev)
++{
++ struct sk_buff *skb;
++ printk("aic %s\n", __func__);
++
++ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
++ if (IS_ERR(skb)) {
++ printk("HCI reset during shutdown failed\n");
++ return PTR_ERR(skb);
++ }
++ kfree_skb(skb);
++
++ return 0;
++}
++#endif
++#endif //(CONFIG_BLUEDROID == 0)
++
++static int btusb_open(struct hci_dev *hdev)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ int err = 0;
++
++ AICBT_INFO("%s: Start", __func__);
++
++ err = usb_autopm_get_interface(data->intf);
++ if (err < 0)
++ return err;
++
++ data->intf->needs_remote_wakeup = 1;
++
++#if (CONFIG_BLUEDROID == 0)
++ //err = download_patch(data->fw_info,1);
++ printk(" download_patch %d", err);
++ if (err < 0) {
++ goto failed;
++ }
++#endif
++
++
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)){
++ goto done;
++ }
++
++ if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)){
++ goto done;
++ }
++
++ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
++ if (err < 0)
++ goto failed;
++
++ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
++ if (err < 0) {
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->intr_anchor);
++ goto failed;
++ }
++
++ set_bit(BTUSB_BULK_RUNNING, &data->flags);
++ btusb_submit_bulk_urb(hdev, GFP_KERNEL);
++
++done:
++ usb_autopm_put_interface(data->intf);
++ AICBT_INFO("%s: End", __func__);
++ return 0;
++
++failed:
++ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ usb_autopm_put_interface(data->intf);
++ AICBT_ERR("%s: Failed", __func__);
++ return err;
++}
++
++static void btusb_stop_traffic(struct btusb_data *data)
++{
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->intr_anchor);
++ usb_kill_anchored_urbs(&data->bulk_anchor);
++ usb_kill_anchored_urbs(&data->isoc_anchor);
++}
++
++static int btusb_close(struct hci_dev *hdev)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
++ int i;
++#endif
++ int err;
++
++ AICBT_INFO("%s: hci running %lu", __func__, hdev->flags & HCI_RUNNING);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)){
++ return 0;
++ }
++
++ if (!test_and_clear_bit(BTUSB_INTR_RUNNING, &data->flags)){
++ return 0;
++ }
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
++ for (i = 0; i < NUM_REASSEMBLY; i++) {
++ if (hdev->reassembly[i]) {
++ AICBT_DBG("%s: free ressembly[%d]", __func__, i);
++ kfree_skb(hdev->reassembly[i]);
++ hdev->reassembly[i] = NULL;
++ }
++ }
++#endif
++
++ cancel_work_sync(&data->work);
++ cancel_work_sync(&data->waker);
++
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
++ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++
++ btusb_stop_traffic(data);
++ err = usb_autopm_get_interface(data->intf);
++ if (err < 0)
++ goto failed;
++
++ data->intf->needs_remote_wakeup = 0;
++ usb_autopm_put_interface(data->intf);
++
++failed:
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_scuttle_anchored_urbs(&data->deferred);
++ return 0;
++}
++
++static int btusb_flush(struct hci_dev *hdev)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++
++ AICBT_DBG("%s", __func__);
++
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->tx_anchor);
++
++ return 0;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++static void btusb_isoc_snd_tx_complete(struct urb *urb);
++
++static int snd_send_sco_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ //struct usb_ctrlrequest *dr;
++ struct urb *urb;
++ unsigned int pipe;
++ int err;
++
++ AICBT_DBG("%s:pkt type %d, packet_len : %d",
++ __func__,bt_cb(skb)->pkt_type, skb->len);
++
++ if (!hdev && !test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1) {
++ kfree(skb);
++ return -ENODEV;
++ }
++
++ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!urb) {
++ AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
++ kfree(skb);
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
++
++ usb_fill_int_urb(urb, data->udev, pipe,
++ skb->data, skb->len, btusb_isoc_snd_tx_complete,
++ skb, data->isoc_tx_ep->bInterval);
++
++ urb->transfer_flags = URB_ISO_ASAP;
++
++ fill_isoc_descriptor(urb, skb->len,
++ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
++
++ hdev->stat.sco_tx++;
++
++ usb_anchor_urb(urb, &data->tx_anchor);
++
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d",
++ __func__, urb, bt_cb(skb)->pkt_type, err);
++ kfree(urb->setup_packet);
++ usb_unanchor_urb(urb);
++ } else
++ usb_mark_last_busy(data->udev);
++ usb_free_urb(urb);
++
++ return err;
++
++}
++
++static bool snd_copy_send_sco_data( AIC_sco_card_t *pSCOSnd)
++{
++ struct snd_pcm_runtime *runtime = pSCOSnd->playback.substream->runtime;
++ unsigned int frame_bytes = 2, frames1;
++ const u8 *source;
++
++ snd_pcm_uframes_t period_size = runtime->period_size;
++ int i, count;
++ u8 buffer[period_size * 3];
++ int sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
++ struct sk_buff *skb;
++
++ count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
++ skb = bt_skb_alloc(((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count), GFP_ATOMIC);
++ skb->dev = (void *)hci_dev_get(0);
++ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
++ skb_put(skb, ((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count));
++ if(!skb)
++ return false;
++
++ AICBT_DBG("%s, buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
++ sco_packet_bytes, count);
++
++ source = runtime->dma_area + pSCOSnd->playback.buffer_pos * frame_bytes;
++
++ if (pSCOSnd->playback.buffer_pos + period_size <= runtime->buffer_size) {
++ memcpy(buffer, source, period_size * frame_bytes);
++ } else {
++ /* wrap around at end of ring buffer */
++ frames1 = runtime->buffer_size - pSCOSnd->playback.buffer_pos;
++ memcpy(buffer, source, frames1 * frame_bytes);
++ memcpy(&buffer[frames1 * frame_bytes],
++ runtime->dma_area, (period_size - frames1) * frame_bytes);
++ }
++
++ pSCOSnd->playback.buffer_pos += period_size;
++ if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
++ pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
++
++ for(i = 0; i < count; i++) {
++ *((__u16 *)(skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE))) = pSCOSnd->usb_data->sco_handle;
++ *((__u8 *)(skb->data + i*(sco_packet_bytes + HCI_SCO_HDR_SIZE) + 2)) = sco_packet_bytes;
++ memcpy((skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE) + HCI_SCO_HDR_SIZE),
++ &buffer[sco_packet_bytes * i], sco_packet_bytes);
++ }
++
++ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ snd_pcm_period_elapsed(pSCOSnd->playback.substream);
++ }
++ snd_send_sco_frame(skb);
++ return true;
++}
++
++static void btusb_isoc_snd_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = urb->context;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++
++ AICBT_DBG("%s: status %d count %d",
++ __func__,urb->status, urb->actual_length);
++
++ if (skb && hdev) {
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto done;
++
++ if (!urb->status)
++ hdev->stat.byte_tx += urb->transfer_buffer_length;
++ else
++ hdev->stat.err_tx++;
++ } else
++ AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
++
++done:
++ kfree(urb->setup_packet);
++ kfree_skb(skb);
++ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)){
++ snd_copy_send_sco_data(pSCOSnd);
++ //schedule_work(&pSCOSnd->send_sco_work);
++ }
++}
++
++static void playback_work(struct work_struct *work)
++{
++ AIC_sco_card_t *pSCOSnd = container_of(work, AIC_sco_card_t, send_sco_work);
++
++ snd_copy_send_sco_data(pSCOSnd);
++}
++
++#endif
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0))
++int btusb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++#else
++int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
++{
++#endif
++ //struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ struct usb_ctrlrequest *dr;
++ struct urb *urb;
++ unsigned int pipe;
++ int err = 0;
++ int retries = 0;
++ u16 *opcode = NULL;
++
++ AICBT_DBG("%s: hdev %p, btusb data %p, pkt type %d",
++ __func__, hdev, data, bt_cb(skb)->pkt_type);
++
++ //printk("aic %d %d\r\n", bt_cb(skb)->pkt_type, skb->len);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
++ skb->dev = (void *)hdev;
++#endif
++#endif
++
++ switch (bt_cb(skb)->pkt_type) {
++ case HCI_COMMAND_PKT:
++ print_command(skb);
++ urb = usb_alloc_urb(0, GFP_ATOMIC);
++ if (!urb)
++ return -ENOMEM;
++
++ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
++ if (!dr) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ dr->bRequestType = data->cmdreq_type;
++ dr->bRequest = 0;
++ dr->wIndex = 0;
++ dr->wValue = 0;
++ dr->wLength = __cpu_to_le16(skb->len);
++
++ pipe = usb_sndctrlpipe(data->udev, 0x00);
++
++ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
++ skb->data, skb->len, btusb_tx_complete, skb);
++
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
++ print_command(skb);
++ opcode = (u16*)(skb->data);
++ printk("aic cmd:0x%04x", *opcode);
++ } else {
++ print_acl(skb, 1);
++ }
++ if (!data->bulk_tx_ep)
++ return -ENODEV;
++
++ urb = usb_alloc_urb(0, GFP_ATOMIC);
++ if (!urb)
++ return -ENOMEM;
++
++ pipe = usb_sndbulkpipe(data->udev,
++ data->bulk_tx_ep->bEndpointAddress);
++
++ usb_fill_bulk_urb(urb, data->udev, pipe,
++ skb->data, skb->len, btusb_tx_complete, skb);
++
++ hdev->stat.acl_tx++;
++ break;
++
++ case HCI_SCODATA_PKT:
++ print_sco(skb, 1);
++ if (!data->isoc_tx_ep || SCO_NUM < 1) {
++ kfree(skb);
++ return -ENODEV;
++ }
++
++ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!urb) {
++ AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
++ kfree(skb);
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
++
++ usb_fill_int_urb(urb, data->udev, pipe,
++ skb->data, skb->len, btusb_isoc_tx_complete,
++ skb, data->isoc_tx_ep->bInterval);
++
++ urb->transfer_flags = URB_ISO_ASAP;
++
++ fill_isoc_descriptor(urb, skb->len,
++ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
++
++ hdev->stat.sco_tx++;
++ goto skip_waking;
++
++ default:
++ return -EILSEQ;
++ }
++
++ err = inc_tx(data);
++ if (err) {
++ usb_anchor_urb(urb, &data->deferred);
++ schedule_work(&data->waker);
++ err = 0;
++ goto done;
++ }
++
++skip_waking:
++ usb_anchor_urb(urb, &data->tx_anchor);
++retry:
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d, retries %d",
++ __func__, urb, bt_cb(skb)->pkt_type, err, retries);
++ if ((bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) && (retries < 10)) {
++ mdelay(1);
++
++ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)
++ print_error_command(skb);
++ retries++;
++ goto retry;
++ }
++ kfree(urb->setup_packet);
++ usb_unanchor_urb(urb);
++ } else
++ usb_mark_last_busy(data->udev);
++ usb_free_urb(urb);
++
++done:
++ return err;
++}
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++static void btusb_destruct(struct hci_dev *hdev)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++
++ AICBT_DBG("%s: name %s", __func__, hdev->name);
++
++ kfree(data);
++}
++#endif
++
++static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++
++ AICBT_DBG("%s: name %s, evt %d", __func__, hdev->name, evt);
++
++ if (SCO_NUM != data->sco_num) {
++ data->sco_num = SCO_NUM;
++ schedule_work(&data->work);
++ }
++}
++
++static inline int set_isoc_interface(struct hci_dev *hdev, int altsetting)
++{
++ struct btusb_data *data = GET_DRV_DATA(hdev);
++ struct usb_interface *intf = data->isoc;
++ struct usb_endpoint_descriptor *ep_desc;
++ int i, err;
++
++ if (!data->isoc)
++ return -ENODEV;
++
++ err = usb_set_interface(data->udev, 1, altsetting);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to set interface, altsetting %d, err %d",
++ __func__, altsetting, err);
++ return err;
++ }
++
++ data->isoc_altsetting = altsetting;
++
++ data->isoc_tx_ep = NULL;
++ data->isoc_rx_ep = NULL;
++
++ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
++ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
++
++ if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
++ data->isoc_tx_ep = ep_desc;
++ continue;
++ }
++
++ if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
++ data->isoc_rx_ep = ep_desc;
++ continue;
++ }
++ }
++
++ if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
++ AICBT_ERR("%s: Invalid SCO descriptors", __func__);
++ return -ENODEV;
++ }
++
++ AICBT_ERR("%s: hdev->reassembly implemant\r\n",
++ __func__);
++
++#if CONFIG_BLUEDROID
++ if(hdev->reassembly[HCI_SCODATA_PKT - 1]) {
++ kfree_skb(hdev->reassembly[HCI_SCODATA_PKT - 1]);
++ hdev->reassembly[HCI_SCODATA_PKT - 1] = NULL;
++ }
++#endif
++ return 0;
++}
++
++static void set_select_msbc(enum CODEC_TYPE type)
++{
++ printk("%s codec type = %d", __func__, (int)type);
++ codec_type = type;
++}
++
++static enum CODEC_TYPE check_select_msbc(void)
++{
++ return codec_type;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++static int check_controller_support_msbc( struct usb_device *udev)
++{
++ //fix this in the future,when new card support msbc decode and encode
++ AICBT_INFO("%s:pid = 0x%02x, vid = 0x%02x",__func__,udev->descriptor.idProduct, udev->descriptor.idVendor);
++ switch (udev->descriptor.idProduct) {
++
++ default:
++ return 0;
++ }
++ return 0;
++}
++#endif
++static void btusb_work(struct work_struct *work)
++{
++ struct btusb_data *data = container_of(work, struct btusb_data, work);
++ struct hci_dev *hdev = data->hdev;
++ int err;
++ int new_alts;
++#ifdef CONFIG_SCO_OVER_HCI
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++#endif
++ printk("%s data->sco_num:%d \r\n", __func__, data->sco_num);
++
++ if (data->sco_num > 0) {
++ if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
++ err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
++ if (err < 0) {
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->isoc_anchor);
++ printk("%s usb_kill_anchored_urbs after \r\n", __func__);
++ return;
++ }
++
++ set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
++ }
++
++ hdev->voice_setting = 93;
++ AICBT_INFO("%s voice settings = 0x%04x", __func__, hdev->voice_setting);
++ if (!(hdev->voice_setting & 0x0003)) {
++ if(data->sco_num == 1)
++ if(check_select_msbc()) {
++ new_alts = 1;
++ } else {
++ new_alts = 2;
++ }
++ else {
++ AICBT_INFO("%s: we don't support mutiple sco link for cvsd", __func__);
++ return;
++ }
++ } else{
++ if(check_select_msbc()) {
++ if(data->sco_num == 1)
++ new_alts = 1;
++ else {
++ AICBT_INFO("%s: we don't support mutiple sco link for msbc", __func__);
++ return;
++ }
++ } else {
++ new_alts = 2;
++ }
++ }
++ if (data->isoc_altsetting != new_alts) {
++
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->isoc_anchor);
++
++ printk("%s set_isoc_interface in \r\n", __func__);
++ if (set_isoc_interface(hdev, new_alts) < 0)
++ return;
++
++ }
++
++ printk("%s set_isoc_interface out \r\n", __func__);
++
++ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++ printk("%s btusb_submit_isoc_urb\r\n", __func__);
++ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++ else
++ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
++ }
++#ifdef CONFIG_SCO_OVER_HCI
++ if(test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++ set_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
++ set_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
++ }
++ if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ schedule_work(&pSCOSnd->send_sco_work);
++ AICBT_INFO("%s: play_timer restart", __func__);
++ }
++#endif
++ } else {
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++#ifdef CONFIG_SCO_OVER_HCI
++ clear_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
++ clear_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
++ //AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++ if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(30));
++ AICBT_INFO("%s: play_timer start", __func__);
++ }
++#endif
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->isoc_anchor);
++
++ set_isoc_interface(hdev, 0);
++ if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
++ usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
++ }
++}
++
++static void btusb_waker(struct work_struct *work)
++{
++ struct btusb_data *data = container_of(work, struct btusb_data, waker);
++ int err;
++
++ AICBT_DBG("%s", __func__);
++
++ err = usb_autopm_get_interface(data->intf);
++ if (err < 0)
++ return;
++
++ usb_autopm_put_interface(data->intf);
++}
++
++int bt_pm_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
++{
++ struct btusb_data *data;
++ firmware_info *fw_info;
++ struct usb_device *udev;
++
++ AICBT_INFO("%s: pm event %ld", __func__, pm_event);
++
++ data = container_of(notifier, struct btusb_data, pm_notifier);
++ fw_info = data->fw_info;
++ udev = fw_info->udev;
++
++ switch (pm_event) {
++ case PM_SUSPEND_PREPARE:
++ case PM_HIBERNATION_PREPARE:
++#if 0
++ patch_entry->fw_len = load_firmware(fw_info, &patch_entry->fw_cache);
++ if (patch_entry->fw_len <= 0) {
++ /* We may encount failure in loading firmware, just give a warning */
++ AICBT_WARN("%s: Failed to load firmware", __func__);
++ }
++#endif
++ if (!device_may_wakeup(&udev->dev)) {
++#if (CONFIG_RESET_RESUME || CONFIG_BLUEDROID)
++ AICBT_INFO("%s:remote wakeup not supported, reset resume supported", __func__);
++#else
++ fw_info->intf->needs_binding = 1;
++ AICBT_INFO("%s:remote wakeup not supported, binding needed", __func__);
++#endif
++ }
++ break;
++
++ case PM_POST_SUSPEND:
++ case PM_POST_HIBERNATION:
++ case PM_POST_RESTORE:
++#if 0
++ /* Reclaim fw buffer when bt usb resumed */
++ if (patch_entry->fw_len > 0) {
++ kfree(patch_entry->fw_cache);
++ patch_entry->fw_cache = NULL;
++ patch_entry->fw_len = 0;
++ }
++#endif
++
++#if BTUSB_RPM
++ usb_disable_autosuspend(udev);
++ usb_enable_autosuspend(udev);
++ pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
++#endif
++ break;
++
++ default:
++ break;
++ }
++
++ return NOTIFY_DONE;
++}
++
++int bt_reboot_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
++{
++ struct btusb_data *data;
++ firmware_info *fw_info;
++ struct usb_device *udev;
++
++ AICBT_INFO("%s: pm event %ld", __func__, pm_event);
++
++ data = container_of(notifier, struct btusb_data, reboot_notifier);
++ fw_info = data->fw_info;
++ udev = fw_info->udev;
++
++ switch (pm_event) {
++ case SYS_DOWN:
++ AICBT_DBG("%s:system down or restart", __func__);
++ break;
++
++ case SYS_HALT:
++ case SYS_POWER_OFF:
++#if SUSPNED_DW_FW
++ cancel_work_sync(&data->work);
++
++ btusb_stop_traffic(data);
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->tx_anchor);
++
++
++ if(fw_info_4_suspend) {
++ download_suspend_patch(fw_info_4_suspend,1);
++ }
++ else
++ AICBT_ERR("%s: Failed to download suspend fw", __func__);
++#endif
++
++#ifdef SET_WAKEUP_DEVICE
++ set_wakeup_device_from_conf(fw_info_4_suspend);
++#endif
++ AICBT_DBG("%s:system halt or power off", __func__);
++ break;
++
++ default:
++ break;
++ }
++
++ return NOTIFY_DONE;
++}
++
++
++#ifdef CONFIG_SCO_OVER_HCI
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++void aic_snd_capture_timeout(ulong data)
++#else
++void aic_snd_capture_timeout(struct timer_list *t)
++#endif
++{
++ uint8_t null_data[255];
++ struct btusb_data *usb_data;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++ usb_data = (struct btusb_data *)data;
++#else
++ usb_data = &snd_cap_timer.snd_usb_data;
++#endif
++ aic_copy_capture_data_to_alsa(usb_data, null_data, snd_cap_timer.snd_sco_length/2);
++ //printk("%s enter\r\n", __func__);
++ mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++void aic_snd_play_timeout(ulong data)
++#else
++void aic_snd_play_timeout(struct timer_list *t)
++#endif
++{
++ AIC_sco_card_t *pSCOSnd;
++ struct snd_pcm_runtime *runtime;
++ snd_pcm_uframes_t period_size;
++ int count;
++ struct btusb_data *usb_data;
++ int sco_packet_bytes;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++ usb_data = (struct btusb_data *)data;
++#else
++ usb_data = &snd_cap_timer.snd_usb_data;
++#endif
++ pSCOSnd = usb_data->pSCOSnd;
++
++ if(test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ return;
++ }
++
++ if(!test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ return;
++ }
++
++ runtime = pSCOSnd->playback.substream->runtime;
++ period_size = runtime->period_size;
++ sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
++ count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
++
++ pSCOSnd->playback.buffer_pos += period_size;
++ if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
++ pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
++
++ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ snd_pcm_period_elapsed(pSCOSnd->playback.substream);
++ }
++ //AICBT_DBG("%s,play_timer restart buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
++ //sco_packet_bytes, count);
++ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3*count));
++}
++
++static const struct snd_pcm_hardware snd_card_sco_capture_default =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
++ SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
++ .rates = (SNDRV_PCM_RATE_8000),
++ .rate_min = 8000,
++ .rate_max = 8000,
++ .channels_min = 1,
++ .channels_max = 1,
++ .buffer_bytes_max = 8 * 768,
++ .period_bytes_min = 48,
++ .period_bytes_max = 768,
++ .periods_min = 1,
++ .periods_max = 8,
++ .fifo_size = 8,
++
++};
++
++static int snd_sco_capture_pcm_open(struct snd_pcm_substream * substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ AICBT_INFO("%s", __FUNCTION__);
++ pSCOSnd->capture.substream = substream;
++
++ memcpy(&substream->runtime->hw, &snd_card_sco_capture_default, sizeof(struct snd_pcm_hardware));
++ pSCOSnd->capture.buffer_pos = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++ init_timer(&snd_cap_timer.cap_timer);
++ snd_cap_timer.cap_timer.data = (unsigned long)pSCOSnd->usb_data;
++ snd_cap_timer.cap_timer.function = aic_snd_capture_timeout;
++#else
++ timer_setup(&snd_cap_timer.cap_timer, aic_snd_capture_timeout, 0);
++ snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
++#endif
++
++ if(check_controller_support_msbc(pSCOSnd->dev)) {
++ substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
++ substream->runtime->hw.rate_max = 16000;
++ substream->runtime->hw.period_bytes_min = 96;
++ substream->runtime->hw.period_bytes_max = 16 * 96;
++ substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
++ }
++ set_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
++ return 0;
++}
++
++static int snd_sco_capture_pcm_close(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ del_timer(&snd_cap_timer.cap_timer);
++ clear_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
++ return 0;
++}
++
++static int snd_sco_capture_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
++{
++ AICBT_DBG("%s, cmd = %d", __FUNCTION__, cmd);
++ switch (cmd)
++ {
++ default:
++ return snd_pcm_lib_ioctl(substream, cmd, arg);
++ }
++ return 0;
++}
++
++static int snd_sco_capture_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
++{
++
++ int err;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
++ AICBT_INFO("%s,err : %d, runtime state : %d", __FUNCTION__, err, runtime->status->state);
++ return err;
++}
++
++static int snd_sco_capture_pcm_hw_free(struct snd_pcm_substream * substream)
++{
++ AICBT_DBG("%s", __FUNCTION__);
++ return snd_pcm_lib_free_vmalloc_buffer(substream);;
++}
++
++static int snd_sco_capture_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++
++ AICBT_INFO("%s %d\n", __FUNCTION__, (int)runtime->period_size);
++ if (test_bit(DISCONNECTED, &pSCOSnd->states))
++ return -ENODEV;
++ if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
++ return -EIO;
++
++ if(runtime->rate == 8000) {
++ if(pSCOSnd->usb_data->isoc_altsetting != 2)
++ return -ENOEXEC;
++ pSCOSnd->capture.sco_packet_bytes = 48;
++ }
++ else if(runtime->rate == 16000 && check_controller_support_msbc(pSCOSnd->dev)) {
++ if(pSCOSnd->usb_data->isoc_altsetting != 4)
++ return -ENOEXEC;
++ pSCOSnd->capture.sco_packet_bytes = 96;
++ }
++ else if(pSCOSnd->usb_data->isoc_altsetting == 2) {
++ pSCOSnd->capture.sco_packet_bytes = 48;
++ }
++ else if(pSCOSnd->usb_data->isoc_altsetting == 1) {
++ pSCOSnd->capture.sco_packet_bytes = 24;
++ }
++ return 0;
++}
++
++static int snd_sco_capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++ AICBT_INFO("%s, cmd : %d", __FUNCTION__, cmd);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
++ return -EIO;
++ set_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
++ return 0;
++ case SNDRV_PCM_TRIGGER_STOP:
++ clear_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++static snd_pcm_uframes_t snd_sco_capture_pcm_pointer(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ return pSCOSnd->capture.buffer_pos;
++}
++
++
++static struct snd_pcm_ops snd_sco_capture_pcm_ops = {
++ .open = snd_sco_capture_pcm_open,
++ .close = snd_sco_capture_pcm_close,
++ .ioctl = snd_sco_capture_ioctl,
++ .hw_params = snd_sco_capture_pcm_hw_params,
++ .hw_free = snd_sco_capture_pcm_hw_free,
++ .prepare = snd_sco_capture_pcm_prepare,
++ .trigger = snd_sco_capture_pcm_trigger,
++ .pointer = snd_sco_capture_pcm_pointer,
++};
++
++
++static const struct snd_pcm_hardware snd_card_sco_playback_default =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
++ SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ .rates = (SNDRV_PCM_RATE_8000),
++ .rate_min = 8000,
++ .rate_max = 8000,
++ .channels_min = 1,
++ .channels_max = 1,
++ .buffer_bytes_max = 8 * 768,
++ .period_bytes_min = 48,
++ .period_bytes_max = 768,
++ .periods_min = 1,
++ .periods_max = 8,
++ .fifo_size = 8,
++};
++
++static int snd_sco_playback_pcm_open(struct snd_pcm_substream * substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++ int err = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++ init_timer(&snd_cap_timer.play_timer);
++ snd_cap_timer.play_timer.data = (unsigned long)pSCOSnd->usb_data;
++ snd_cap_timer.play_timer.function = aic_snd_play_timeout;
++#else
++ timer_setup(&snd_cap_timer.play_timer, aic_snd_play_timeout, 0);
++ snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
++#endif
++ pSCOSnd->playback.buffer_pos = 0;
++
++ AICBT_INFO("%s, rate : %d", __FUNCTION__, substream->runtime->rate);
++ memcpy(&substream->runtime->hw, &snd_card_sco_playback_default, sizeof(struct snd_pcm_hardware));
++ if(check_controller_support_msbc(pSCOSnd->dev)) {
++ substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
++ substream->runtime->hw.rate_max = 16000;
++ substream->runtime->hw.period_bytes_min = 96;
++ substream->runtime->hw.period_bytes_max = 16 * 96;
++ substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
++ }
++ pSCOSnd->playback.substream = substream;
++ set_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
++
++ return err;
++}
++
++static int snd_sco_playback_pcm_close(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ del_timer(&snd_cap_timer.play_timer);
++ AICBT_INFO("%s: play_timer delete", __func__);
++ clear_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
++ cancel_work_sync(&pSCOSnd->send_sco_work);
++ return 0;
++}
++
++static int snd_sco_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
++{
++ AICBT_DBG("%s, cmd : %d", __FUNCTION__, cmd);
++ switch (cmd)
++ {
++ default:
++ return snd_pcm_lib_ioctl(substream, cmd, arg);
++ break;
++ }
++ return 0;
++}
++
++static int snd_sco_playback_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
++{
++ int err;
++ err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
++ return err;
++}
++
++static int snd_sco_palyback_pcm_hw_free(struct snd_pcm_substream * substream)
++{
++ AICBT_DBG("%s", __FUNCTION__);
++ return snd_pcm_lib_free_vmalloc_buffer(substream);
++}
++
++static int snd_sco_playback_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++
++ AICBT_INFO("%s, bound_rate = %d", __FUNCTION__, runtime->rate);
++
++ if (test_bit(DISCONNECTED, &pSCOSnd->states))
++ return -ENODEV;
++ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
++ return -EIO;
++
++ if(runtime->rate == 8000) {
++ if(pSCOSnd->usb_data->isoc_altsetting != 2)
++ return -ENOEXEC;
++ pSCOSnd->playback.sco_packet_bytes = 48;
++ }
++ else if(runtime->rate == 16000) {
++ if(pSCOSnd->usb_data->isoc_altsetting != 4)
++ return -ENOEXEC;
++ pSCOSnd->playback.sco_packet_bytes = 96;
++ }
++
++ return 0;
++}
++
++static int snd_sco_playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ AICBT_INFO("%s, cmd = %d", __FUNCTION__, cmd);
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
++ return -EIO;
++ set_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
++ schedule_work(&pSCOSnd->send_sco_work);
++#ifdef CONFIG_SCO_OVER_HCI
++ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++ AICBT_INFO("%s: play_timer cmd 1 start ", __func__);
++ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3));
++ }
++#endif
++ return 0;
++ case SNDRV_PCM_TRIGGER_STOP:
++ clear_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++static snd_pcm_uframes_t snd_sco_playback_pcm_pointer(struct snd_pcm_substream *substream)
++{
++ AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++ return pSCOSnd->playback.buffer_pos;
++}
++
++
++static struct snd_pcm_ops snd_sco_playback_pcm_ops = {
++ .open = snd_sco_playback_pcm_open,
++ .close = snd_sco_playback_pcm_close,
++ .ioctl = snd_sco_playback_ioctl,
++ .hw_params = snd_sco_playback_pcm_hw_params,
++ .hw_free = snd_sco_palyback_pcm_hw_free,
++ .prepare = snd_sco_playback_pcm_prepare,
++ .trigger = snd_sco_playback_pcm_trigger,
++ .pointer = snd_sco_playback_pcm_pointer,
++};
++
++
++static AIC_sco_card_t* btusb_snd_init(struct usb_interface *intf, const struct usb_device_id *id, struct btusb_data *data)
++{
++ struct snd_card *card;
++ AIC_sco_card_t *pSCOSnd;
++ int err=0;
++ AICBT_INFO("%s", __func__);
++ err = snd_card_new(&intf->dev,
++ -1, AIC_SCO_ID, THIS_MODULE,
++ sizeof(AIC_sco_card_t), &card);
++ if (err < 0) {
++ AICBT_ERR("%s: sco snd card create fail", __func__);
++ return NULL;
++ }
++ // private data
++ pSCOSnd = (AIC_sco_card_t *)card->private_data;
++ pSCOSnd->card = card;
++ pSCOSnd->dev = interface_to_usbdev(intf);
++ pSCOSnd->usb_data = data;
++
++ strcpy(card->driver, AIC_SCO_ID);
++ strcpy(card->shortname, "Aicsemi sco snd");
++ sprintf(card->longname, "Aicsemi sco over hci: VID:0x%04x, PID:0x%04x",
++ id->idVendor, pSCOSnd->dev->descriptor.idProduct);
++
++ err = snd_pcm_new(card, AIC_SCO_ID, 0, 1, 1, &pSCOSnd->pcm);
++ if (err < 0) {
++ AICBT_ERR("%s: sco snd card new pcm fail", __func__);
++ return NULL;
++ }
++ pSCOSnd->pcm->private_data = pSCOSnd;
++ sprintf(pSCOSnd->pcm->name, "sco_pcm:VID:0x%04x, PID:0x%04x",
++ id->idVendor, pSCOSnd->dev->descriptor.idProduct);
++
++ snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sco_playback_pcm_ops);
++ snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sco_capture_pcm_ops);
++
++ err = snd_card_register(card);
++ if (err < 0) {
++ AICBT_ERR("%s: sco snd card register card fail", __func__);
++ return NULL;
++ }
++
++ spin_lock_init(&pSCOSnd->capture_lock);
++ spin_lock_init(&pSCOSnd->playback_lock);
++ INIT_WORK(&pSCOSnd->send_sco_work, playback_work);
++ return pSCOSnd;
++}
++#endif
++
++static int aicwf_usb_chipmatch(u16 vid, u16 pid){
++
++ if(pid == USB_PRODUCT_ID_AIC8801){
++ g_chipid = PRODUCT_ID_AIC8801;
++ printk("%s USE AIC8801\r\n", __func__);
++ return 0;
++ }else if(pid == USB_PRODUCT_ID_AIC8800DC){
++ g_chipid = PRODUCT_ID_AIC8800DC;
++ printk("%s USE AIC8800DC\r\n", __func__);
++ return 0;
++ }else if(pid == USB_PRODUCT_ID_AIC8800D80){
++ g_chipid = PRODUCT_ID_AIC8800D80;
++ printk("%s USE AIC8800D80\r\n", __func__);
++ return 0;
++ }else{
++ return -1;
++ }
++}
++
++
++static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
++{
++ struct usb_device *udev = interface_to_usbdev(intf);
++ struct usb_endpoint_descriptor *ep_desc;
++ u8 endpoint_num;
++ struct btusb_data *data;
++ struct hci_dev *hdev;
++ firmware_info *fw_info;
++ int i, err=0;
++
++ bt_support = 1;
++
++ AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d, idVendor 0x%04x, "
++ "idProduct 0x%04x", __func__, intf,
++ intf->cur_altsetting->desc.bInterfaceNumber,
++ id->idVendor, id->idProduct);
++
++ aicwf_usb_chipmatch(id->idVendor, id->idProduct);
++
++ /* interface numbers are hardcoded in the spec */
++ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++ return -ENODEV;
++
++ AICBT_DBG("%s: can wakeup = %x, may wakeup = %x", __func__,
++ device_can_wakeup(&udev->dev), device_may_wakeup(&udev->dev));
++
++ data = aic_alloc(intf);
++ if (!data)
++ return -ENOMEM;
++
++ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
++ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
++
++ endpoint_num = usb_endpoint_num(ep_desc);
++ printk("endpoint num %d\n", endpoint_num);
++
++ if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
++ data->intr_ep = ep_desc;
++ continue;
++ }
++
++ if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
++ data->bulk_tx_ep = ep_desc;
++ continue;
++ }
++
++ if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
++ data->bulk_rx_ep = ep_desc;
++ continue;
++ }
++ }
++
++ if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
++ aic_free(data);
++ return -ENODEV;
++ }
++
++ data->cmdreq_type = USB_TYPE_CLASS;
++
++ data->udev = udev;
++ data->intf = intf;
++
++ dlfw_dis_state = 0;
++ spin_lock_init(&queue_lock);
++ spin_lock_init(&dlfw_lock);
++ spin_lock_init(&data->lock);
++
++ INIT_WORK(&data->work, btusb_work);
++ INIT_WORK(&data->waker, btusb_waker);
++ spin_lock_init(&data->txlock);
++
++ init_usb_anchor(&data->tx_anchor);
++ init_usb_anchor(&data->intr_anchor);
++ init_usb_anchor(&data->bulk_anchor);
++ init_usb_anchor(&data->isoc_anchor);
++ init_usb_anchor(&data->deferred);
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++ spin_lock_init(&data->rxlock);
++ data->recv_bulk = btusb_recv_bulk;
++#endif
++#endif
++
++
++ fw_info = firmware_info_init(intf);
++ if (fw_info)
++ data->fw_info = fw_info;
++ else {
++ AICBT_WARN("%s: Failed to initialize fw info", __func__);
++ /* Skip download patch */
++ goto end;
++ }
++
++ AICBT_INFO("%s: download begining...", __func__);
++
++#if CONFIG_BLUEDROID
++ mutex_lock(&btchr_mutex);
++#endif
++ if(g_chipid == PRODUCT_ID_AIC8800DC){
++ err = download_patch(data->fw_info,1);
++ }
++
++#if CONFIG_BLUEDROID
++ mutex_unlock(&btchr_mutex);
++#endif
++
++ AICBT_INFO("%s: download ending...", __func__);
++ if (err < 0) {
++ return err;
++ }
++
++
++ hdev = hci_alloc_dev();
++ if (!hdev) {
++ aic_free(data);
++ data = NULL;
++ return -ENOMEM;
++ }
++
++ HDEV_BUS = HCI_USB;
++
++ data->hdev = hdev;
++
++ SET_HCIDEV_DEV(hdev, &intf->dev);
++
++ hdev->open = btusb_open;
++ hdev->close = btusb_close;
++ hdev->flush = btusb_flush;
++ hdev->send = btusb_send_frame;
++ hdev->notify = btusb_notify;
++#if (CONFIG_BLUEDROID == 0)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
++ hdev->shutdown = btusb_shutdown;
++#endif
++#endif //(CONFIG_BLUEDROIF == 0)
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
++ hci_set_drvdata(hdev, data);
++#else
++ hdev->driver_data = data;
++ hdev->destruct = btusb_destruct;
++ hdev->owner = THIS_MODULE;
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
++ if (!reset_on_close){
++ /* set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); */
++ AICBT_DBG("%s: Set HCI_QUIRK_RESET_ON_CLOSE", __func__);
++ }
++#endif
++
++ /* Interface numbers are hardcoded in the specification */
++ data->isoc = usb_ifnum_to_if(data->udev, 1);
++ if (data->isoc) {
++ err = usb_driver_claim_interface(&btusb_driver,
++ data->isoc, data);
++ if (err < 0) {
++ hci_free_dev(hdev);
++ hdev = NULL;
++ aic_free(data);
++ data = NULL;
++ return err;
++ }
++#ifdef CONFIG_SCO_OVER_HCI
++ data->pSCOSnd = btusb_snd_init(intf, id, data);
++#endif
++ }
++
++ err = hci_register_dev(hdev);
++ if (err < 0) {
++ hci_free_dev(hdev);
++ hdev = NULL;
++ aic_free(data);
++ data = NULL;
++ return err;
++ }
++
++ usb_set_intfdata(intf, data);
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
++ data->early_suspend.suspend = btusb_early_suspend;
++ data->early_suspend.resume = btusb_late_resume;
++ register_early_suspend(&data->early_suspend);
++#else
++ data->pm_notifier.notifier_call = bt_pm_notify;
++ data->reboot_notifier.notifier_call = bt_reboot_notify;
++ register_pm_notifier(&data->pm_notifier);
++ register_reboot_notifier(&data->reboot_notifier);
++#endif
++
++#if CONFIG_BLUEDROID
++ AICBT_INFO("%s: Check bt reset flag %d", __func__, bt_reset);
++ /* Report hci hardware error after everthing is ready,
++ * especially hci register is completed. Or, btchr_poll
++ * will get null hci dev when hotplug in.
++ */
++ if (bt_reset == 1) {
++ hci_hardware_error();
++ bt_reset = 0;
++ } else
++ bt_reset = 0; /* Clear and reset it anyway */
++#endif
++
++end:
++ return 0;
++}
++
++static void btusb_disconnect(struct usb_interface *intf)
++{
++ struct btusb_data *data;
++ struct hci_dev *hdev = NULL;
++#if CONFIG_BLUEDROID
++ wait_event_interruptible(bt_dlfw_wait, (check_set_dlfw_state_value(2) == 2));
++#endif
++
++ bt_support = 0;
++
++ AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d",
++ __func__, intf, intf->cur_altsetting->desc.bInterfaceNumber);
++
++ data = usb_get_intfdata(intf);
++
++ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++ return;
++
++ if (data)
++ hdev = data->hdev;
++ else {
++ AICBT_WARN("%s: Failed to get bt usb data[Null]", __func__);
++ return;
++ }
++
++#ifdef CONFIG_SCO_OVER_HCI
++ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
++ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++ if(!pSCOSnd) {
++ AICBT_ERR("%s: sco private data is null", __func__);
++ return;
++ }
++ set_bit(DISCONNECTED, &pSCOSnd->states);
++ snd_card_disconnect(pSCOSnd->card);
++ snd_card_free_when_closed(pSCOSnd->card);
++ }
++#endif
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++ unregister_early_suspend(&data->early_suspend);
++#else
++ unregister_pm_notifier(&data->pm_notifier);
++ unregister_reboot_notifier(&data->reboot_notifier);
++#endif
++
++ firmware_info_destroy(intf);
++
++#if CONFIG_BLUEDROID
++ if (test_bit(HCI_RUNNING, &hdev->flags)) {
++ AICBT_INFO("%s: Set BT reset flag", __func__);
++ bt_reset = 1;
++ }
++#endif
++
++ usb_set_intfdata(data->intf, NULL);
++
++ if (data->isoc)
++ usb_set_intfdata(data->isoc, NULL);
++
++ hci_unregister_dev(hdev);
++
++ if (intf == data->isoc)
++ usb_driver_release_interface(&btusb_driver, data->intf);
++ else if (data->isoc)
++ usb_driver_release_interface(&btusb_driver, data->isoc);
++
++#if !CONFIG_BLUEDROID
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++ __hci_dev_put(hdev);
++#endif
++#endif
++
++ hci_free_dev(hdev);
++ aic_free(data);
++ data = NULL;
++ set_dlfw_state_value(0);
++}
++
++#ifdef CONFIG_PM
++static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct btusb_data *data = usb_get_intfdata(intf);
++ //firmware_info *fw_info = data->fw_info;
++
++ AICBT_INFO("%s: event 0x%x, suspend count %d", __func__,
++ message.event, data->suspend_count);
++
++ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++ return 0;
++#if 0
++ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
++ set_bt_onoff(fw_info, 1);
++#endif
++ if (data->suspend_count++)
++ return 0;
++
++ spin_lock_irq(&data->txlock);
++ if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
++ set_bit(BTUSB_SUSPENDING, &data->flags);
++ spin_unlock_irq(&data->txlock);
++ } else {
++ spin_unlock_irq(&data->txlock);
++ data->suspend_count--;
++ AICBT_ERR("%s: Failed to enter suspend", __func__);
++ return -EBUSY;
++ }
++
++ cancel_work_sync(&data->work);
++
++ btusb_stop_traffic(data);
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_kill_anchored_urbs(&data->tx_anchor);
++
++ return 0;
++}
++
++static void play_deferred(struct btusb_data *data)
++{
++ struct urb *urb;
++ int err;
++
++ while ((urb = usb_get_from_anchor(&data->deferred))) {
++ usb_anchor_urb(urb, &data->tx_anchor);
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ AICBT_ERR("%s: Failed to submit urb %p, err %d",
++ __func__, urb, err);
++ kfree(urb->setup_packet);
++ usb_unanchor_urb(urb);
++ } else {
++ usb_mark_last_busy(data->udev);
++ }
++ usb_free_urb(urb);
++
++ data->tx_in_flight++;
++ }
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_scuttle_anchored_urbs(&data->deferred);
++}
++
++static int btusb_resume(struct usb_interface *intf)
++{
++ struct btusb_data *data = usb_get_intfdata(intf);
++ struct hci_dev *hdev = data->hdev;
++ int err = 0;
++
++ AICBT_INFO("%s: Suspend count %d", __func__, data->suspend_count);
++
++ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++ return 0;
++
++ if (--data->suspend_count)
++ return 0;
++
++ #if 0
++ /*check_fw_version to check the status of the BT Controller after USB Resume*/
++ err = check_fw_version(fw_info);
++ if (err !=0)
++ {
++ AICBT_INFO("%s: BT Controller Power OFF And Return hci_hardware_error:%d", __func__, err);
++ hci_hardware_error();
++ }
++ #endif
++
++ AICBT_INFO("%s g_chipid %x\n", __func__, g_chipid);
++ if(g_chipid == PRODUCT_ID_AIC8800DC){
++ if(data->fw_info){
++ err = download_patch(data->fw_info,1);
++ }else{
++ AICBT_WARN("%s: Failed to initialize fw info", __func__);
++ }
++ }
++
++ #if 1
++ if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
++ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
++ if (err < 0) {
++ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++ goto failed;
++ }
++ }
++ #endif
++
++ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
++ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
++ if (err < 0) {
++ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
++ goto failed;
++ }
++
++ btusb_submit_bulk_urb(hdev, GFP_NOIO);
++ }
++
++ if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++ else
++ btusb_submit_isoc_urb(hdev, GFP_NOIO);
++ }
++
++ spin_lock_irq(&data->txlock);
++ play_deferred(data);
++ clear_bit(BTUSB_SUSPENDING, &data->flags);
++ spin_unlock_irq(&data->txlock);
++ schedule_work(&data->work);
++
++ return 0;
++
++failed:
++ mdelay(URB_CANCELING_DELAY_MS);
++ usb_scuttle_anchored_urbs(&data->deferred);
++ spin_lock_irq(&data->txlock);
++ clear_bit(BTUSB_SUSPENDING, &data->flags);
++ spin_unlock_irq(&data->txlock);
++
++ return err;
++}
++#endif
++
++static struct usb_driver btusb_driver = {
++ .name = "aic_btusb",
++ .probe = btusb_probe,
++ .disconnect = btusb_disconnect,
++#ifdef CONFIG_PM
++ .suspend = btusb_suspend,
++ .resume = btusb_resume,
++#if CONFIG_RESET_RESUME
++ .reset_resume = btusb_resume,
++#endif
++#endif
++ .id_table = btusb_table,
++ .supports_autosuspend = 1,
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
++ .disable_hub_initiated_lpm = 1,
++#endif
++};
++
++static int __init btusb_init(void)
++{
++ int err;
++
++ AICBT_INFO("AICBT_RELEASE_NAME: %s",AICBT_RELEASE_NAME);
++ AICBT_INFO("AicSemi Bluetooth USB driver module init, version %s", VERSION);
++ AICBT_INFO("RELEASE DATE: 2023_0506_1635 \r\n");
++#if CONFIG_BLUEDROID
++ err = btchr_init();
++ if (err < 0) {
++ /* usb register will go on, even bt char register failed */
++ AICBT_ERR("Failed to register usb char device interfaces");
++ } else
++ bt_char_dev_registered = 1;
++#endif
++ err = usb_register(&btusb_driver);
++ if (err < 0)
++ AICBT_ERR("Failed to register aic bluetooth USB driver");
++ return err;
++}
++
++static void __exit btusb_exit(void)
++{
++ AICBT_INFO("AicSemi Bluetooth USB driver module exit");
++#if CONFIG_BLUEDROID
++ if (bt_char_dev_registered > 0)
++ btchr_exit();
++#endif
++ usb_deregister(&btusb_driver);
++}
++
++module_init(btusb_init);
++module_exit(btusb_exit);
++
++
++module_param(mp_drv_mode, int, 0644);
++MODULE_PARM_DESC(mp_drv_mode, "0: NORMAL; 1: MP MODE");
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
++MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
++#endif
++
++MODULE_AUTHOR("AicSemi Corporation");
++MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb.h
+@@ -0,0 +1,753 @@
++/*
++ *
++ * Aic Bluetooth USB driver
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/skbuff.h>
++#include <linux/errno.h>
++#include <linux/usb.h>
++#include <linux/cdev.h>
++#include <linux/device.h>
++#include <linux/poll.h>
++
++#include <linux/version.h>
++#include <linux/pm_runtime.h>
++#include <linux/firmware.h>
++#include <linux/suspend.h>
++
++
++#ifdef CONFIG_PLATFORM_UBUNTU
++#define CONFIG_BLUEDROID 0 /* bleuz 0, bluedroid 1 */
++#else
++#define CONFIG_BLUEDROID 1 /* bleuz 0, bluedroid 1 */
++#endif
++
++
++//#define CONFIG_SCO_OVER_HCI
++#define CONFIG_USB_AIC_UART_SCO_DRIVER
++
++#ifdef CONFIG_SCO_OVER_HCI
++#include <linux/usb/audio.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++
++#define AIC_SCO_ID "snd_sco_aic"
++enum {
++ USB_CAPTURE_RUNNING,
++ USB_PLAYBACK_RUNNING,
++ ALSA_CAPTURE_OPEN,
++ ALSA_PLAYBACK_OPEN,
++ ALSA_CAPTURE_RUNNING,
++ ALSA_PLAYBACK_RUNNING,
++ CAPTURE_URB_COMPLETED,
++ PLAYBACK_URB_COMPLETED,
++ DISCONNECTED,
++};
++
++// AIC sound card
++typedef struct AIC_sco_card {
++ struct snd_card *card;
++ struct snd_pcm *pcm;
++ struct usb_device *dev;
++ struct btusb_data *usb_data;
++ unsigned long states;
++ struct aic_sco_stream {
++ struct snd_pcm_substream *substream;
++ unsigned int sco_packet_bytes;
++ snd_pcm_uframes_t buffer_pos;
++ } capture, playback;
++ spinlock_t capture_lock;
++ spinlock_t playback_lock;
++ struct work_struct send_sco_work;
++} AIC_sco_card_t;
++#endif
++/* Some Android system may use standard Linux kernel, while
++ * standard Linux may also implement early suspend feature.
++ * So exclude earysuspend.h from CONFIG_BLUEDROID.
++ */
++#ifdef CONFIG_HAS_EARLYSUSPEND
++#include <linux/earlysuspend.h>
++#endif
++
++#if CONFIG_BLUEDROID
++#else
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/hci.h>
++#endif
++
++
++/***********************************
++** AicSemi - For aic_btusb driver **
++***********************************/
++#define URB_CANCELING_DELAY_MS 10
++/* when OS suspended, module is still powered,usb is not powered,
++ * this may set to 1, and must comply with special patch code.
++ */
++#define CONFIG_RESET_RESUME 1
++#define PRINT_CMD_EVENT 0
++#define PRINT_ACL_DATA 0
++#define PRINT_SCO_DATA 0
++
++#define AICBT_DBG_FLAG 0
++
++#if AICBT_DBG_FLAG
++#define AICBT_DBG(fmt, arg...) printk( "aic_btusb: " fmt "\n" , ## arg)
++#else
++#define AICBT_DBG(fmt, arg...)
++#endif
++
++#define AICBT_INFO(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++#define AICBT_WARN(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++#define AICBT_ERR(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
++#define HDEV_BUS hdev->bus
++#define USB_RPM 1
++#else
++#define HDEV_BUS hdev->type
++#define USB_RPM 0
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
++#define NUM_REASSEMBLY 3
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
++#define GET_DRV_DATA(x) hci_get_drvdata(x)
++#else
++#define GET_DRV_DATA(x) x->driver_data
++#endif
++
++#define SCO_NUM hdev->conn_hash.sco_num
++
++
++#define BTUSB_RPM (0 * USB_RPM) /* 1 SS enable; 0 SS disable */
++#define BTUSB_WAKEUP_HOST 0 /* 1 enable; 0 disable */
++#define BTUSB_MAX_ISOC_FRAMES 48
++#define BTUSB_INTR_RUNNING 0
++#define BTUSB_BULK_RUNNING 1
++#define BTUSB_ISOC_RUNNING 2
++#define BTUSB_SUSPENDING 3
++#define BTUSB_DID_ISO_RESUME 4
++
++#define HCI_VENDOR_USB_DISC_HARDWARE_ERROR 0xFF
++
++#define HCI_CMD_READ_BD_ADDR 0x1009
++#define HCI_VENDOR_READ_LMP_VERISION 0x1001
++#define HCI_VENDOR_RESET 0x0C03
++
++#define DRV_NORMAL_MODE 0
++#define DRV_MP_MODE 1
++int mp_drv_mode = 0; /* 1 Mptool Fw; 0 Normal Fw */
++
++
++#if CONFIG_BLUEDROID
++#define QUEUE_SIZE 500
++
++/***************************************
++** AicSemi - Integrate from bluetooth.h **
++*****************************************/
++/* Reserv for core and drivers use */
++#define BT_SKB_RESERVE 8
++
++/* BD Address */
++typedef struct {
++ __u8 b[6];
++} __packed bdaddr_t;
++
++/* Skb helpers */
++struct bt_skb_cb {
++ __u8 pkt_type;
++ __u8 incoming;
++ __u16 expect;
++ __u16 tx_seq;
++ __u8 retries;
++ __u8 sar;
++ __u8 force_active;
++};
++
++#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
++
++static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
++{
++ struct sk_buff *skb;
++
++ if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) {
++ skb_reserve(skb, BT_SKB_RESERVE);
++ bt_cb(skb)->incoming = 0;
++ }
++ return skb;
++}
++/* AicSemi - Integrate from bluetooth.h end */
++
++/***********************************
++** AicSemi - Integrate from hci.h **
++***********************************/
++#define HCI_MAX_ACL_SIZE 1024
++#define HCI_MAX_SCO_SIZE 255
++#define HCI_MAX_EVENT_SIZE 260
++#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
++
++/* HCI bus types */
++#define HCI_VIRTUAL 0
++#define HCI_USB 1
++#define HCI_PCCARD 2
++#define HCI_UART 3
++#define HCI_RS232 4
++#define HCI_PCI 5
++#define HCI_SDIO 6
++
++/* HCI controller types */
++#define HCI_BREDR 0x00
++#define HCI_AMP 0x01
++
++/* HCI device flags */
++enum {
++ HCI_UP,
++ HCI_INIT,
++ HCI_RUNNING,
++
++ HCI_PSCAN,
++ HCI_ISCAN,
++ HCI_AUTH,
++ HCI_ENCRYPT,
++ HCI_INQUIRY,
++
++ HCI_RAW,
++
++ HCI_RESET,
++};
++
++/*
++ * BR/EDR and/or LE controller flags: the flags defined here should represent
++ * states from the controller.
++ */
++enum {
++ HCI_SETUP,
++ HCI_AUTO_OFF,
++ HCI_MGMT,
++ HCI_PAIRABLE,
++ HCI_SERVICE_CACHE,
++ HCI_LINK_KEYS,
++ HCI_DEBUG_KEYS,
++ HCI_UNREGISTER,
++
++ HCI_LE_SCAN,
++ HCI_SSP_ENABLED,
++ HCI_HS_ENABLED,
++ HCI_LE_ENABLED,
++ HCI_CONNECTABLE,
++ HCI_DISCOVERABLE,
++ HCI_LINK_SECURITY,
++ HCI_PENDING_CLASS,
++};
++
++/* HCI data types */
++#define HCI_COMMAND_PKT 0x01
++#define HCI_ACLDATA_PKT 0x02
++#define HCI_SCODATA_PKT 0x03
++#define HCI_EVENT_PKT 0x04
++#define HCI_VENDOR_PKT 0xff
++
++#define HCI_MAX_NAME_LENGTH 248
++#define HCI_MAX_EIR_LENGTH 240
++
++#define HCI_OP_READ_LOCAL_VERSION 0x1001
++struct hci_rp_read_local_version {
++ __u8 status;
++ __u8 hci_ver;
++ __le16 hci_rev;
++ __u8 lmp_ver;
++ __le16 manufacturer;
++ __le16 lmp_subver;
++} __packed;
++
++#define HCI_EV_CMD_COMPLETE 0x0e
++struct hci_ev_cmd_complete {
++ __u8 ncmd;
++ __le16 opcode;
++} __packed;
++
++/* ---- HCI Packet structures ---- */
++#define HCI_COMMAND_HDR_SIZE 3
++#define HCI_EVENT_HDR_SIZE 2
++#define HCI_ACL_HDR_SIZE 4
++#define HCI_SCO_HDR_SIZE 3
++
++struct hci_command_hdr {
++ __le16 opcode; /* OCF & OGF */
++ __u8 plen;
++} __packed;
++
++struct hci_event_hdr {
++ __u8 evt;
++ __u8 plen;
++} __packed;
++
++struct hci_acl_hdr {
++ __le16 handle; /* Handle & Flags(PB, BC) */
++ __le16 dlen;
++} __packed;
++
++struct hci_sco_hdr {
++ __le16 handle;
++ __u8 dlen;
++} __packed;
++
++static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb)
++{
++ return (struct hci_event_hdr *) skb->data;
++}
++
++static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb)
++{
++ return (struct hci_acl_hdr *) skb->data;
++}
++
++static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
++{
++ return (struct hci_sco_hdr *) skb->data;
++}
++
++/* ---- HCI Ioctl requests structures ---- */
++struct hci_dev_stats {
++ __u32 err_rx;
++ __u32 err_tx;
++ __u32 cmd_tx;
++ __u32 evt_rx;
++ __u32 acl_tx;
++ __u32 acl_rx;
++ __u32 sco_tx;
++ __u32 sco_rx;
++ __u32 byte_rx;
++ __u32 byte_tx;
++};
++/* AicSemi - Integrate from hci.h end */
++
++/*****************************************
++** AicSemi - Integrate from hci_core.h **
++*****************************************/
++struct hci_conn_hash {
++ struct list_head list;
++ unsigned int acl_num;
++ unsigned int sco_num;
++ unsigned int le_num;
++};
++
++#define HCI_MAX_SHORT_NAME_LENGTH 10
++
++#define NUM_REASSEMBLY 4
++struct hci_dev {
++ struct mutex lock;
++
++ char name[8];
++ unsigned long flags;
++ __u16 id;
++ __u8 bus;
++ __u8 dev_type;
++
++ struct sk_buff *reassembly[NUM_REASSEMBLY];
++
++ struct hci_conn_hash conn_hash;
++
++ struct hci_dev_stats stat;
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++ atomic_t refcnt;
++ struct module *owner;
++ void *driver_data;
++#endif
++
++ atomic_t promisc;
++
++ struct device *parent;
++ struct device dev;
++
++ unsigned long dev_flags;
++
++ int (*open)(struct hci_dev *hdev);
++ int (*close)(struct hci_dev *hdev);
++ int (*flush)(struct hci_dev *hdev);
++ int (*send)(struct sk_buff *skb);
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++ void (*destruct)(struct hci_dev *hdev);
++#endif
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
++ __u16 voice_setting;
++#endif
++ void (*notify)(struct hci_dev *hdev, unsigned int evt);
++ int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
++ u8 *align_data;
++};
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
++{
++ atomic_inc(&d->refcnt);
++ return d;
++}
++
++static inline void __hci_dev_put(struct hci_dev *d)
++{
++ if (atomic_dec_and_test(&d->refcnt))
++ d->destruct(d);
++}
++#endif
++
++static inline void *hci_get_drvdata(struct hci_dev *hdev)
++{
++ return dev_get_drvdata(&hdev->dev);
++}
++
++static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
++{
++ dev_set_drvdata(&hdev->dev, data);
++}
++
++#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
++/* AicSemi - Integrate from hci_core.h end */
++
++/* ----- HCI Commands ---- */
++#define HCI_OP_INQUIRY 0x0401
++#define HCI_OP_INQUIRY_CANCEL 0x0402
++#define HCI_OP_EXIT_PERIODIC_INQ 0x0404
++#define HCI_OP_CREATE_CONN 0x0405
++#define HCI_OP_DISCONNECT 0x0406
++#define HCI_OP_ADD_SCO 0x0407
++#define HCI_OP_CREATE_CONN_CANCEL 0x0408
++#define HCI_OP_ACCEPT_CONN_REQ 0x0409
++#define HCI_OP_REJECT_CONN_REQ 0x040a
++#define HCI_OP_LINK_KEY_REPLY 0x040b
++#define HCI_OP_LINK_KEY_NEG_REPLY 0x040c
++#define HCI_OP_PIN_CODE_REPLY 0x040d
++#define HCI_OP_PIN_CODE_NEG_REPLY 0x040e
++#define HCI_OP_CHANGE_CONN_PTYPE 0x040f
++#define HCI_OP_AUTH_REQUESTED 0x0411
++#define HCI_OP_SET_CONN_ENCRYPT 0x0413
++#define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415
++#define HCI_OP_REMOTE_NAME_REQ 0x0419
++#define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a
++#define HCI_OP_READ_REMOTE_FEATURES 0x041b
++#define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c
++#define HCI_OP_READ_REMOTE_VERSION 0x041d
++#define HCI_OP_SETUP_SYNC_CONN 0x0428
++#define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429
++#define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a
++#define HCI_OP_SNIFF_MODE 0x0803
++#define HCI_OP_EXIT_SNIFF_MODE 0x0804
++#define HCI_OP_ROLE_DISCOVERY 0x0809
++#define HCI_OP_SWITCH_ROLE 0x080b
++#define HCI_OP_READ_LINK_POLICY 0x080c
++#define HCI_OP_WRITE_LINK_POLICY 0x080d
++#define HCI_OP_READ_DEF_LINK_POLICY 0x080e
++#define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f
++#define HCI_OP_SNIFF_SUBRATE 0x0811
++#define HCI_OP_Write_Link_Policy_Settings 0x080d
++#define HCI_OP_SET_EVENT_MASK 0x0c01
++#define HCI_OP_RESET 0x0c03
++#define HCI_OP_SET_EVENT_FLT 0x0c05
++#define HCI_OP_Write_Extended_Inquiry_Response 0x0c52
++#define HCI_OP_Write_Simple_Pairing_Mode 0x0c56
++#define HCI_OP_Read_Buffer_Size 0x1005
++#define HCI_OP_Host_Buffer_Size 0x0c33
++#define HCI_OP_Read_Local_Version_Information 0x1001
++#define HCI_OP_Read_BD_ADDR 0x1009
++#define HCI_OP_Read_Local_Supported_Commands 0x1002
++#define HCI_OP_Write_Scan_Enable 0x0c1a
++#define HCI_OP_Write_Current_IAC_LAP 0x0c3a
++#define HCI_OP_Write_Inquiry_Scan_Activity 0x0c1e
++#define HCI_OP_Write_Class_of_Device 0x0c24
++#define HCI_OP_LE_Rand 0x2018
++#define HCI_OP_LE_Set_Random_Address 0x2005
++#define HCI_OP_LE_Set_Extended_Scan_Enable 0x2042
++#define HCI_OP_LE_Set_Extended_Scan_Parameters 0x2041
++#define HCI_OP_Set_Event_Filter 0x0c05
++#define HCI_OP_Write_Voice_Setting 0x0c26
++#define HCI_OP_Change_Local_Name 0x0c13
++#define HCI_OP_Read_Local_Name 0x0c14
++#define HCI_OP_Wirte_Page_Timeout 0x0c18
++#define HCI_OP_LE_Clear_Resolving_List 0x0c29
++#define HCI_OP_LE_Set_Addres_Resolution_Enable_Command 0x0c2e
++#define HCI_OP_Write_Inquiry_mode 0x0c45
++#define HCI_OP_Write_Page_Scan_Type 0x0c47
++#define HCI_OP_Write_Inquiry_Scan_Type 0x0c43
++
++#define HCI_OP_Delete_Stored_Link_Key 0x0c12
++#define HCI_OP_LE_Read_Local_Resolvable_Address 0x202d
++#define HCI_OP_LE_Extended_Create_Connection 0x2043
++#define HCI_OP_Read_Remote_Version_Information 0x041d
++#define HCI_OP_LE_Start_Encryption 0x2019
++#define HCI_OP_LE_Add_Device_to_Resolving_List 0x2027
++#define HCI_OP_LE_Set_Privacy_Mode 0x204e
++#define HCI_OP_LE_Connection_Update 0x2013
++
++/* ----- HCI events---- */
++#define HCI_OP_DISCONNECT 0x0406
++#define HCI_EV_INQUIRY_COMPLETE 0x01
++#define HCI_EV_INQUIRY_RESULT 0x02
++#define HCI_EV_CONN_COMPLETE 0x03
++#define HCI_EV_CONN_REQUEST 0x04
++#define HCI_EV_DISCONN_COMPLETE 0x05
++#define HCI_EV_AUTH_COMPLETE 0x06
++#define HCI_EV_REMOTE_NAME 0x07
++#define HCI_EV_ENCRYPT_CHANGE 0x08
++#define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09
++
++#define HCI_EV_REMOTE_FEATURES 0x0b
++#define HCI_EV_REMOTE_VERSION 0x0c
++#define HCI_EV_QOS_SETUP_COMPLETE 0x0d
++#define HCI_EV_CMD_COMPLETE 0x0e
++#define HCI_EV_CMD_STATUS 0x0f
++
++#define HCI_EV_ROLE_CHANGE 0x12
++#define HCI_EV_NUM_COMP_PKTS 0x13
++#define HCI_EV_MODE_CHANGE 0x14
++#define HCI_EV_PIN_CODE_REQ 0x16
++#define HCI_EV_LINK_KEY_REQ 0x17
++#define HCI_EV_LINK_KEY_NOTIFY 0x18
++#define HCI_EV_CLOCK_OFFSET 0x1c
++#define HCI_EV_PKT_TYPE_CHANGE 0x1d
++#define HCI_EV_PSCAN_REP_MODE 0x20
++
++#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22
++#define HCI_EV_REMOTE_EXT_FEATURES 0x23
++#define HCI_EV_SYNC_CONN_COMPLETE 0x2c
++#define HCI_EV_SYNC_CONN_CHANGED 0x2d
++#define HCI_EV_SNIFF_SUBRATE 0x2e
++#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f
++#define HCI_EV_IO_CAPA_REQUEST 0x31
++#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36
++#define HCI_EV_REMOTE_HOST_FEATURES 0x3d
++#define HCI_EV_LE_Meta 0x3e
++
++#define CONFIG_MAC_OFFSET_GEN_1_2 (0x3C) //MAC's OFFSET in config/efuse for aic generation 1~2 bluetooth chip
++#define CONFIG_MAC_OFFSET_GEN_3PLUS (0x44) //MAC's OFFSET in config/efuse for aic generation 3+ bluetooth chip
++
++
++typedef struct {
++ uint16_t vid;
++ uint16_t pid;
++ uint16_t lmp_sub_default;
++ uint16_t lmp_sub;
++ uint16_t eversion;
++ char *mp_patch_name;
++ char *patch_name;
++ char *config_name;
++ uint8_t *fw_cache;
++ int fw_len;
++ uint16_t mac_offset;
++ uint32_t max_patch_size;
++} patch_info;
++
++//Define ioctl cmd the same as HCIDEVUP in the kernel
++#define DOWN_FW_CFG _IOW('E', 176, int)
++//#ifdef CONFIG_SCO_OVER_HCI
++//#define SET_ISO_CFG _IOW('H', 202, int)
++//#else
++#define SET_ISO_CFG _IOW('E', 177, int)
++//#endif
++#define RESET_CONTROLLER _IOW('E', 178, int)
++#define DWFW_CMPLT _IOW('E', 179, int)
++
++#define GET_USB_INFO _IOR('E', 180, int)
++
++/* for altsettings*/
++#include <linux/fs.h>
++#define BDADDR_FILE "/data/misc/bluetooth/bdaddr"
++#define FACTORY_BT_BDADDR_STORAGE_LEN 17
++#if 0
++static inline int getmacaddr(uint8_t * vnd_local_bd_addr)
++{
++ struct file *bdaddr_file;
++ mm_segment_t oldfs;
++ char buf[FACTORY_BT_BDADDR_STORAGE_LEN];
++ int32_t i = 0;
++ memset(buf, 0, FACTORY_BT_BDADDR_STORAGE_LEN);
++ bdaddr_file = filp_open(BDADDR_FILE, O_RDONLY, 0);
++ if (IS_ERR(bdaddr_file)){
++ AICBT_INFO("No Mac Config for BT\n");
++ return -1;
++ }
++ oldfs = get_fs();
++ set_fs(KERNEL_DS);
++ bdaddr_file->f_op->llseek(bdaddr_file, 0, 0);
++ bdaddr_file->f_op->read(bdaddr_file, buf, FACTORY_BT_BDADDR_STORAGE_LEN, &bdaddr_file->f_pos);
++ for (i = 0; i < 6; i++) {
++ if(buf[3*i]>'9')
++ {
++ if(buf[3*i]>'Z')
++ buf[3*i] -=('a'-'A'); //change a to A
++ buf[3*i] -= ('A'-'9'-1);
++ }
++ if(buf[3*i+1]>'9')
++ {
++ if(buf[3*i+1]>'Z')
++ buf[3*i+1] -=('a'-'A'); //change a to A
++ buf[3*i+1] -= ('A'-'9'-1);
++ }
++ vnd_local_bd_addr[5-i] = ((uint8_t)buf[3*i]-'0')*16 + ((uint8_t)buf[3*i+1]-'0');
++ }
++ set_fs(oldfs);
++ filp_close(bdaddr_file, NULL);
++ return 0;
++}
++#endif
++
++#endif /* CONFIG_BLUEDROID */
++
++
++typedef struct {
++ struct usb_interface *intf;
++ struct usb_device *udev;
++ int pipe_in, pipe_out;
++ uint8_t *send_pkt;
++ uint8_t *rcv_pkt;
++ struct hci_command_hdr *cmd_hdr;
++ struct hci_event_hdr *evt_hdr;
++ struct hci_ev_cmd_complete *cmd_cmp;
++ uint8_t *req_para, *rsp_para;
++ uint8_t *fw_data;
++ int pkt_len;
++ int fw_len;
++} firmware_info;
++
++/*******************************
++** Reasil patch code
++********************************/
++#define CMD_CMP_EVT 0x0e
++#define RCV_PKT_LEN 64
++#define SEND_PKT_LEN 300
++#define MSG_TO 1000
++#define PATCH_SEG_MAX 252
++#define DATA_END 0x80
++#define DOWNLOAD_OPCODE 0xfc02
++#define HCI_VSC_UPDATE_PT_CMD 0xFC75
++#define BTOFF_OPCODE 0xfc28
++#define TRUE 1
++#define FALSE 0
++#define CMD_HDR_LEN sizeof(struct hci_command_hdr)
++#define EVT_HDR_LEN sizeof(struct hci_event_hdr)
++#define CMD_CMP_LEN sizeof(struct hci_ev_cmd_complete)
++#define MAX_PATCH_SIZE_24K (1024*24)
++#define MAX_PATCH_SIZE_40K (1024*40)
++
++
++#define FW_RAM_ADID_BASE_ADDR 0x101788
++#define FW_RAM_PATCH_BASE_ADDR 0x184000
++#define FW_ADID_BASE_NAME "fw_adid_8800dc.bin"
++#define FW_PATCH_TABLE_NAME "fw_patch_table_8800dc.bin"
++#define FW_PATCH_BASE_NAME "fw_patch_8800dc.bin"
++#define FW_PATCH_TABLE_NAME_U02 "fw_patch_table_8800dc_u02.bin"
++#define FW_PATCH_BASE_NAME_U02 "fw_patch_8800dc_u02.bin"
++#define FW_PATCH_TABLE_NAME_U02H "fw_patch_table_8800dc_u02h.bin"
++#define FW_PATCH_BASE_NAME_U02H "fw_patch_8800dc_u02h.bin"
++#define AICBT_PT_TAG "AICBT_PT_TAG"
++
++enum aicbt_patch_table_type {
++ AICBT_PT_NULL = 0x00,
++ AICBT_PT_TRAP,
++ AICBT_PT_B4,
++ AICBT_PT_BTMODE,
++ AICBT_PT_PWRON,
++ AICBT_PT_AF,
++ AICBT_PT_VER,
++ AICBT_PT_MAX,
++};
++
++#define HCI_VSC_FW_STATUS_GET_CMD 0xFC78
++
++struct fw_status {
++ u8 status;
++} __packed;
++
++#define HCI_PATCH_DATA_MAX_LEN 240
++#define HCI_VSC_MEM_WR_SIZE 240
++#define HCI_VSC_MEM_RD_SIZE 128
++#define HCI_VSC_UPDATE_PT_SIZE 249
++#define HCI_PT_MAX_LEN 31
++
++#define HCI_VSC_DBG_RD_MEM_CMD 0xFC01
++
++struct hci_dbg_rd_mem_cmd {
++ __le32 start_addr;
++ __u8 type;
++ __u8 length;
++}__attribute__ ((packed));
++
++struct hci_dbg_rd_mem_cmd_evt {
++ __u8 status;
++ __u8 length;
++ __u8 data[HCI_VSC_MEM_RD_SIZE];
++}__attribute__ ((packed));
++
++struct long_buffer_tag {
++ __u8 length;
++ __u8 data[HCI_VSC_MEM_WR_SIZE];
++};
++
++struct hci_dbg_wr_mem_cmd {
++ __le32 start_addr;
++ __u8 type;
++ __u8 length;
++ __u8 data[HCI_VSC_MEM_WR_SIZE];
++};
++
++struct aicbt_patch_table {
++ char *name;
++ uint32_t type;
++ uint32_t *data;
++ uint32_t len;
++ struct aicbt_patch_table *next;
++};
++
++struct aicbt_patch_table_cmd {
++ uint8_t patch_num;
++ uint32_t patch_table_addr[31];
++ uint32_t patch_table_data[31];
++}__attribute__ ((packed));
++
++
++enum aic_endpoit {
++ CTRL_EP = 0,
++ INTR_EP = 3,
++ BULK_EP = 1,
++ ISOC_EP = 4
++};
++
++/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */
++#define HCI_VERSION_CODE LINUX_VERSION_CODE
++
++int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device);
++int aicbt_patch_table_free(struct aicbt_patch_table **head);
++int download_patch(firmware_info *fw_info, int cached);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
++#define NUM_REASSEMBLY 3
++#else
++#define NUM_REASSEMBLY 4
++#endif
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
+@@ -0,0 +1,126 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/usb.h>
++#include <linux/poll.h>
++#include <linux/cdev.h>
++#include <linux/device.h>
++
++
++
++#define IOCTL_CHAR_DEVICE_NAME "aic_btusb_ex_dev"
++
++#define SET_APCF_PARAMETER _IOR('E', 181, int)
++
++static dev_t ioctl_devid; /* bt char device number */
++static struct cdev ioctl_char_dev; /* bt character device structure */
++static struct class *ioctl_char_class; /* device class for usb char driver */
++
++extern struct file_operations ioctl_chrdev_ops;
++
++extern void btchr_external_write(char* data, int len);
++
++static long ioctl_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
++{
++ char data[1024];
++ int ret = 0;
++
++ printk("%s enter\r\n", __func__);
++ memset(data, 0, 1024);
++ switch(cmd)
++ {
++ case SET_APCF_PARAMETER:
++ printk("set apcf parameter\r\n");
++ ret = copy_from_user(data, (int __user *)arg, 1024);
++ btchr_external_write(&data[1], (int)data[0]);
++ break;
++
++ default:
++ printk("unknow cmdr\r\n");
++ break;
++ }
++ return 0;
++}
++
++
++#ifdef CONFIG_COMPAT
++static long compat_ioctlchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ return ioctl_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
++}
++#endif
++
++
++struct file_operations ioctl_chrdev_ops = {
++ unlocked_ioctl : ioctl_ioctl,
++#ifdef CONFIG_COMPAT
++ compat_ioctl : compat_ioctlchr_ioctl,
++#endif
++
++};
++
++static int __init init_extenal_ioctl(void){
++ int res = 0;
++ struct device *dev;
++
++ printk("%s enter\r\n", __func__);
++
++ ioctl_char_class = class_create(THIS_MODULE, IOCTL_CHAR_DEVICE_NAME);
++ if (IS_ERR(ioctl_char_class)) {
++ printk("Failed to create ioctl char class");
++ }
++
++ res = alloc_chrdev_region(&ioctl_devid, 0, 1, IOCTL_CHAR_DEVICE_NAME);
++ if (res < 0) {
++ printk("Failed to allocate ioctl char device");
++ goto err_alloc;
++ }
++
++ dev = device_create(ioctl_char_class, NULL, ioctl_devid, NULL, IOCTL_CHAR_DEVICE_NAME);
++ if (IS_ERR(dev)) {
++ printk("Failed to create ioctl char device");
++ res = PTR_ERR(dev);
++ goto err_create;
++ }
++
++ cdev_init(&ioctl_char_dev, &ioctl_chrdev_ops);
++ res = cdev_add(&ioctl_char_dev, ioctl_devid, 1);
++ if (res < 0) {
++ printk("Failed to add ioctl char device");
++ goto err_add;
++ }
++
++ return res;
++
++err_add:
++ device_destroy(ioctl_char_class, ioctl_devid);
++err_create:
++ unregister_chrdev_region(ioctl_devid, 1);
++err_alloc:
++ class_destroy(ioctl_char_class);
++
++ return res;
++
++}
++static void __exit deinit_extenal_ioctl(void){
++ printk("%s enter\r\n", __func__);
++ device_destroy(ioctl_char_class, ioctl_devid);
++ cdev_del(&ioctl_char_dev);
++ unregister_chrdev_region(ioctl_devid, 1);
++ class_destroy(ioctl_char_class);
++
++}
++
++module_init(init_extenal_ioctl);
++module_exit(deinit_extenal_ioctl);
++
++
++MODULE_AUTHOR("AicSemi Corporation");
++MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
++MODULE_LICENSE("GPL");
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
+@@ -0,0 +1,3 @@
++
++void btchr_external_write(char* data, int len);
++
--- /dev/null
+From fdb2822982887c7048f34601688ae2406ccbcfcc Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 15 Apr 2024 11:29:17 +0800
+Subject: [PATCH 114/116] riscv: dts: starfive: visionfive 2: Sync the sound
+ card names with v5.15 and v6.1
+
+Sync the sound card names. So we can build Debian images with the
+same process.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../dts/starfive/jh7110-starfive-visionfive-2-tdm.dts | 2 +-
+ .../dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts | 2 +-
+ .../boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi | 8 ++++----
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+@@ -8,7 +8,7 @@
+ #include "jh7110-starfive-visionfive-2-v1.3b.dts"
+
+ / {
+- sound-tdm {
++ sound5: snd-card5 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+@@ -9,7 +9,7 @@
+
+ / {
+ /* i2s + wm8960 */
+- sound-wm8960 {
++ sound6: snd-card6 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -91,9 +91,9 @@
+ #sound-dai-cells = <0>;
+ };
+
+- sound-pwmdac {
++ sound3: snd-card3 {
+ compatible = "simple-audio-card";
+- simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++ simple-audio-card,name = "Starfive-PWMDAC-Sound-Card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+@@ -113,12 +113,12 @@
+ };
+ };
+
+- sound-hdmi {
++ sound1: snd-card1 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+- simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++ simple-audio-card,name = "Starfive-HDMI-Sound-Card";
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ format = "i2s";
--- /dev/null
+From dac6f3021895e7678552789edff22f1dc909e274 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 15 Apr 2024 20:59:35 +0800
+Subject: [PATCH 115/116] clk: starfive: jh7110: Change uart3-uart5 clk
+ register info
+
+The core_clk division register of uart3-uart5 include fractional and
+integral parts, but now only use the integral part, so include shift
+operation. The integral part include 8 bit, so the max value can be
+configed is 255.
+
+Signed-off-by: Yanhong Wang <yanhong.wang@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/clk/starfive/clk-starfive-jh71x0.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/clk/starfive/clk-starfive-jh71x0.c
++++ b/drivers/clk/starfive/clk-starfive-jh71x0.c
+@@ -10,6 +10,8 @@
+ #include <linux/device.h>
+ #include <linux/io.h>
+
++#include <dt-bindings/clock/starfive,jh7110-crg.h>
++
+ #include "clk-starfive-jh71x0.h"
+
+ static struct jh71x0_clk *jh71x0_clk_from(struct clk_hw *hw)
+@@ -70,6 +72,11 @@ static unsigned long jh71x0_clk_recalc_r
+ struct jh71x0_clk *clk = jh71x0_clk_from(hw);
+ u32 div = jh71x0_clk_reg_get(clk) & JH71X0_CLK_DIV_MASK;
+
++ if (clk->idx == JH7110_SYSCLK_UART3_CORE ||
++ clk->idx == JH7110_SYSCLK_UART4_CORE ||
++ clk->idx == JH7110_SYSCLK_UART5_CORE)
++ div >>= 8;
++
+ return div ? parent_rate / div : 0;
+ }
+
+@@ -110,6 +117,12 @@ static int jh71x0_clk_set_rate(struct cl
+ unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate),
+ 1UL, (unsigned long)clk->max_div);
+
++ /* UART3-5: [15:8]: integer part of the divisor. [7:0] fraction part of the divisor */
++ if (clk->idx == JH7110_SYSCLK_UART3_CORE ||
++ clk->idx == JH7110_SYSCLK_UART4_CORE ||
++ clk->idx == JH7110_SYSCLK_UART5_CORE)
++ div <<= 8;
++
+ jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, div);
+ return 0;
+ }
--- /dev/null
+From 1ea169bb83d3b556eb9054f1a3e8ce0411370293 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 24 Apr 2024 14:23:07 +0800
+Subject: [PATCH 116/116] riscv: dts: starfive: visionfive 2: Quote
+ corresponding regulators in hdmi and vin
+
+So PMIC can start to work before HDMI and VIN.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -961,6 +961,8 @@
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_pins>;
+ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++ hdmi_0p9-supply = <&hdmi_0p9>;
++ hdmi_1p8-supply = <&hdmi_1p8>;
+
+ hdmi_in: port {
+ #address-cells = <1>;
+@@ -1084,6 +1086,7 @@
+ &vin_sysctl {
+ /* when use dvp open this pinctrl*/
+ status = "okay";
++ mipi_0p9-supply = <&mipi_0p9>;
+
+ ports {
+ #address-cells = <1>;
--- /dev/null
+From b7b4fad8784d7ab4fcd929a56c3e5366046fe7fc Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 14 Oct 2021 20:56:54 +0200
+Subject: [PATCH 1000/1024] serial: 8250_dw: Add starfive,jh7100-hsuart
+ compatible
+
+This adds a compatible for the high speed UARTs on the StarFive JH7100
+RISC-V SoC. Just like the regular uarts we also need to keep the input
+clocks at their default rate and rely only on the divisor in the UART.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/tty/serial/8250/8250_dw.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -802,6 +802,7 @@ static const struct of_device_id dw8250_
+ { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
+ { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
+ { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data },
++ { .compatible = "starfive,jh7100-hsuart", .data = &dw8250_starfive_jh7100_data },
+ { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data },
+ { /* Sentinel */ }
+ };
--- /dev/null
+From 3051ab0102aaf969dd85f07b6ab731dadf97ef6c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 17:13:22 +0100
+Subject: [PATCH 1001/1024] RISC-V: Add StarFive JH7100 audio clock node
+
+Add device tree node for the audio clocks on the StarFive JH7100 RISC-V
+SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -158,6 +158,16 @@
+ riscv,ndev = <133>;
+ };
+
++ audclk: clock-controller@10480000 {
++ compatible = "starfive,jh7100-audclk";
++ reg = <0x0 0x10480000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_AUDIO_SRC>,
++ <&clkgen JH7100_CLK_AUDIO_12288>,
++ <&clkgen JH7100_CLK_DOM7AHB_BUS>;
++ clock-names = "audio_src", "audio_12288", "dom7ahb_bus";
++ #clock-cells = <1>;
++ };
++
+ clkgen: clock-controller@11800000 {
+ compatible = "starfive,jh7100-clkgen";
+ reg = <0x0 0x11800000 0x0 0x10000>;
--- /dev/null
+From 8ef30667e97363ca921416a60e50eb28bf88cc4f Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:11:04 +0800
+Subject: [PATCH 1002/1024] drivers/tty/serial/8250: update driver for JH7100
+
+---
+ drivers/tty/serial/8250/8250_port.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -72,8 +72,16 @@ static const struct serial8250_config ua
+ },
+ [PORT_16550] = {
+ .name = "16550",
++#ifdef CONFIG_SOC_STARFIVE
++ .fifo_size = 16,
++ .tx_loadsz = 16,
++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
++ .rxtrig_bytes = {1, 4, 8, 14},
++ .flags = UART_CAP_FIFO,
++#else
+ .fifo_size = 1,
+ .tx_loadsz = 1,
++#endif
+ },
+ [PORT_16550A] = {
+ .name = "16550A",
--- /dev/null
+From 2d5b71006c6a93e26eb86b7efd37440c020bab46 Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Wed, 17 Nov 2021 14:50:45 +0800
+Subject: [PATCH 1003/1024] dmaengine: dw-axi-dmac: Handle xfer start while
+ non-idle
+
+Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
+Signed-off-by: Curry Zhang <curry.zhang@starfivetech.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 +++++++++++-
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 1 +
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -382,11 +382,13 @@ static void axi_chan_block_xfer_start(st
+ u32 irq_mask;
+ u8 lms = 0; /* Select AXI0 master for LLI fetching */
+
++ chan->is_err = false;
+ if (unlikely(axi_chan_is_hw_enable(chan))) {
+ dev_err(chan2dev(chan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+
+- return;
++ axi_chan_disable(chan);
++ chan->is_err = true;
+ }
+
+ axi_dma_enable(chan->chip);
+@@ -1028,6 +1030,14 @@ static noinline void axi_chan_handle_err
+ axi_chan_name(chan));
+ goto out;
+ }
++ if (chan->is_err) {
++ struct axi_dma_desc *desc = vd_to_axi_desc(vd);
++
++ axi_chan_block_xfer_start(chan, desc);
++ chan->is_err = false;
++ goto out;
++ }
++
+ /* Remove the completed descriptor from issued list */
+ list_del(&vd->node);
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -50,6 +50,7 @@ struct axi_dma_chan {
+ struct dma_slave_config config;
+ enum dma_transfer_direction direction;
+ bool cyclic;
++ bool is_err;
+ /* these other elements are all protected by vc.lock */
+ bool is_paused;
+ };
--- /dev/null
+From 933f8c33d466de20e9894ef8de5b6afe478a420d Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Wed, 17 Nov 2021 14:50:45 +0800
+Subject: [PATCH 1004/1024] dmaengine: dw-axi-dmac: Add StarFive JH7100 support
+
+Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 4 ++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -677,8 +677,13 @@ static int dw_axi_dma_set_hw_desc(struct
+
+ hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1);
+
++#ifdef CONFIG_SOC_STARFIVE
++ ctllo |= DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_DST_MSIZE_POS |
++ DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_SRC_MSIZE_POS;
++#else
+ ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+ DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS;
++#endif
+ hw_desc->lli->ctl_lo = cpu_to_le32(ctllo);
+
+ set_desc_src_master(hw_desc);
+@@ -1502,7 +1507,11 @@ static int dw_probe(struct platform_devi
+ * Therefore, set constraint to 1024 * 4.
+ */
+ dw->dma.dev->dma_parms = &dw->dma_parms;
++#ifdef CONFIG_SOC_STARFIVE
++ dma_set_max_seg_size(&pdev->dev, DMAC_MAX_BLK_SIZE);
++#else
+ dma_set_max_seg_size(&pdev->dev, MAX_BLOCK_SIZE);
++#endif
+ platform_set_drvdata(pdev, chip);
+
+ pm_runtime_enable(chip->dev);
+@@ -1587,6 +1596,9 @@ static const struct of_device_id dw_dma_
+ .compatible = "intel,kmb-axi-dma",
+ .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS,
+ }, {
++ .compatible = "starfive,jh7100-axi-dma",
++ .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
++ }, {
+ .compatible = "starfive,jh7110-axi-dma",
+ .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
+ },
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -284,7 +284,11 @@ enum {
+ #define CH_CTL_L_SRC_MAST BIT(0)
+
+ /* CH_CFG_H */
++#ifdef CONFIG_SOC_STARFIVE
++#define CH_CFG_H_PRIORITY_POS 15
++#else
+ #define CH_CFG_H_PRIORITY_POS 17
++#endif
+ #define CH_CFG_H_DST_PER_POS 12
+ #define CH_CFG_H_SRC_PER_POS 7
+ #define CH_CFG_H_HS_SEL_DST_POS 4
--- /dev/null
+From c5019bf73d1e178beb6055cca254e7d3c916db7c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 17 Jul 2021 21:50:38 +0200
+Subject: [PATCH 1005/1024] pinctrl: starfive: Reset pinmux settings
+
+Current u-boot doesn't seem to take into account that some GPIOs are
+configured as inputs/outputs of certain peripherals on power-up. This
+means it ends up configuring some GPIOs as inputs to more than one
+peripheral which the documentation explicitly says is illegal. Similarly
+it also ends up configuring more than one GPIO as output of the same
+peripheral. While not explicitly mentioned by the documentation this
+also seems like a bad idea.
+
+The easiest way to remedy this mess is to just disconnect all GPIOs from
+peripherals and have our pinmux configuration set everything up
+properly. This, however, means that we'd disconnect the serial console
+from its pins for a while, so add a device tree property to keep
+certain GPIOs from being reset.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../pinctrl/starfive,jh7100-pinctrl.yaml | 4 ++
+ .../starfive/pinctrl-starfive-jh7100.c | 66 +++++++++++++++++++
+ 2 files changed, 70 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
++++ b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
+@@ -88,6 +88,10 @@ properties:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2, 3, 4, 5, 6]
+
++ starfive,keep-gpiomux:
++ description: Keep pinmux for these GPIOs from being reset at boot.
++ $ref: /schemas/types.yaml#/definitions/uint32-array
++
+ required:
+ - compatible
+ - reg
+--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c
++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c
+@@ -203,6 +203,10 @@ static u16 starfive_drive_strength_from_
+ return (clamp(i, 14U, 63U) - 14) / 7;
+ }
+
++static bool keepmux;
++module_param(keepmux, bool, 0644);
++MODULE_PARM_DESC(keepmux, "Keep pinmux settings from previous boot stage");
++
+ struct starfive_pinctrl {
+ struct gpio_chip gc;
+ struct pinctrl_gpio_range gpios;
+@@ -1225,6 +1229,65 @@ static void starfive_disable_clock(void
+ clk_disable_unprepare(data);
+ }
+
++#define GPI_END (GPI_USB_OVER_CURRENT + 1)
++static void starfive_pinmux_reset(struct starfive_pinctrl *sfp)
++{
++ static const DECLARE_BITMAP(defaults, GPI_END) = {
++ BIT_MASK(GPI_I2C0_PAD_SCK_IN) |
++ BIT_MASK(GPI_I2C0_PAD_SDA_IN) |
++ BIT_MASK(GPI_I2C1_PAD_SCK_IN) |
++ BIT_MASK(GPI_I2C1_PAD_SDA_IN) |
++ BIT_MASK(GPI_I2C2_PAD_SCK_IN) |
++ BIT_MASK(GPI_I2C2_PAD_SDA_IN) |
++ BIT_MASK(GPI_I2C3_PAD_SCK_IN) |
++ BIT_MASK(GPI_I2C3_PAD_SDA_IN) |
++ BIT_MASK(GPI_SDIO0_PAD_CARD_DETECT_N) |
++
++ BIT_MASK(GPI_SDIO1_PAD_CARD_DETECT_N) |
++ BIT_MASK(GPI_SPI0_PAD_SS_IN_N) |
++ BIT_MASK(GPI_SPI1_PAD_SS_IN_N) |
++ BIT_MASK(GPI_SPI2_PAD_SS_IN_N) |
++ BIT_MASK(GPI_SPI2AHB_PAD_SS_N) |
++ BIT_MASK(GPI_SPI3_PAD_SS_IN_N),
++
++ BIT_MASK(GPI_UART0_PAD_SIN) |
++ BIT_MASK(GPI_UART1_PAD_SIN) |
++ BIT_MASK(GPI_UART2_PAD_SIN) |
++ BIT_MASK(GPI_UART3_PAD_SIN) |
++ BIT_MASK(GPI_USB_OVER_CURRENT)
++ };
++ DECLARE_BITMAP(keep, NR_GPIOS) = {};
++ struct device_node *np = sfp->gc.parent->of_node;
++ int len = of_property_count_u32_elems(np, "starfive,keep-gpiomux");
++ int i;
++
++ for (i = 0; i < len; i++) {
++ u32 gpio;
++
++ of_property_read_u32_index(np, "starfive,keep-gpiomux", i, &gpio);
++ if (gpio < NR_GPIOS)
++ set_bit(gpio, keep);
++ }
++
++ for (i = 0; i < NR_GPIOS; i++) {
++ if (test_bit(i, keep))
++ continue;
++
++ writel_relaxed(GPO_DISABLE, sfp->base + GPON_DOEN_CFG + 8 * i);
++ writel_relaxed(GPO_LOW, sfp->base + GPON_DOUT_CFG + 8 * i);
++ }
++
++ for (i = 0; i < GPI_END; i++) {
++ void __iomem *reg = sfp->base + GPI_CFG_OFFSET + 4 * i;
++ u32 din = readl_relaxed(reg);
++
++ if (din >= 2 && din < (NR_GPIOS + 2) && test_bit(din - 2, keep))
++ continue;
++
++ writel_relaxed(test_bit(i, defaults), reg);
++ }
++}
++
+ static int starfive_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -1286,6 +1349,9 @@ static int starfive_probe(struct platfor
+ writel(value, sfp->padctl + IO_PADSHARE_SEL);
+ }
+
++ if (!keepmux)
++ starfive_pinmux_reset(sfp);
++
+ value = readl(sfp->padctl + IO_PADSHARE_SEL);
+ switch (value) {
+ case 0:
--- /dev/null
+From a4a2a8886c97e14643fe3e0ab8cf67a75c1bf14d Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Sat, 25 Mar 2023 22:57:06 +0100
+Subject: [PATCH 1006/1024] clk: starfive: Add flags argument to JH71X0__MUX
+ macro
+
+This flag is needed to add the CLK_SET_RATE_PARENT flag on the gmac_tx
+clock on the JH7100, which in turn is needed by the dwmac-starfive
+driver to set the clock properly for 1000, 100 and 10 Mbps links.
+
+This change was mostly made using coccinelle:
+
+@ match @
+expression idx, name, nparents;
+@@
+ JH71X0__MUX(
+-idx, name, nparents,
++idx, name, 0, nparents,
+ ...)
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+---
+ .../clk/starfive/clk-starfive-jh7100-audio.c | 2 +-
+ drivers/clk/starfive/clk-starfive-jh7100.c | 32 +++++++++----------
+ .../clk/starfive/clk-starfive-jh7110-aon.c | 6 ++--
+ .../clk/starfive/clk-starfive-jh7110-isp.c | 2 +-
+ .../clk/starfive/clk-starfive-jh7110-sys.c | 26 +++++++--------
+ drivers/clk/starfive/clk-starfive-jh71x0.h | 4 +--
+ 6 files changed, 36 insertions(+), 36 deletions(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c
+@@ -79,7 +79,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB),
+ JH71X0_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB),
+ JH71X0__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS),
+- JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2,
++ JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 0, 2,
+ JH7100_AUDCLK_VAD_INTMEM,
+ JH7100_AUDCLK_AUDIO_12288),
+ };
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -24,48 +24,48 @@
+ #define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3)
+
+ static const struct jh71x0_clk_data jh7100_clk_data[] __initconst = {
+- JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4,
++ JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 0, 4,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL1_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3,
++ JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 0, 3,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL1_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4,
++ JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 0, 4,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL1_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3,
++ JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 0, 3,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2,
++ JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 0, 2,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL0_OUT),
+- JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2,
++ JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 0, 2,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3,
++ JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 0, 3,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL1_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3,
++ JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 0, 3,
+ JH7100_CLK_OSC_AUD,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL2_OUT),
+ JH71X0_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT),
+- JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3,
++ JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 0, 3,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL1_OUT,
+ JH7100_CLK_PLL2_OUT),
+- JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3,
++ JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 0, 3,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL1_OUT),
+- JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3,
++ JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 0, 3,
+ JH7100_CLK_OSC_AUD,
+ JH7100_CLK_PLL0_OUT,
+ JH7100_CLK_PLL2_OUT),
+@@ -76,7 +76,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC),
+ JH71X0_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT),
+ JH71X0_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC),
+- JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2,
++ JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 0, 2,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_OSC_AUD),
+ JH71X0__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+@@ -142,7 +142,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT),
+ JH71X0_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS),
+ JH71X0__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT),
+- JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2,
++ JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 0, 2,
+ JH7100_CLK_CPU_AXI,
+ JH7100_CLK_NNEBUS_SRC1),
+ JH71X0_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS),
+@@ -166,7 +166,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV),
+ JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32,
+ JH7100_CLK_USBPHY_ROOTDIV),
+- JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2,
++ JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 0, 2,
+ JH7100_CLK_OSC_SYS,
+ JH7100_CLK_USBPHY_PLLDIV25M),
+ JH71X0_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT),
+@@ -200,12 +200,12 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+- JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3,
++ JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 0, 3,
+ JH7100_CLK_GMAC_GTX,
+ JH7100_CLK_GMAC_TX_INV,
+ JH7100_CLK_GMAC_RMII_TX),
+ JH71X0__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX),
+- JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2,
++ JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 0, 2,
+ JH7100_CLK_GMAC_GR_MII_RX,
+ JH7100_CLK_GMAC_RMII_RX),
+ JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE),
+--- a/drivers/clk/starfive/clk-starfive-jh7110-aon.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-aon.c
+@@ -26,7 +26,7 @@
+ static const struct jh71x0_clk_data jh7110_aonclk_data[] = {
+ /* source */
+ JH71X0__DIV(JH7110_AONCLK_OSC_DIV4, "osc_div4", 4, JH7110_AONCLK_OSC),
+- JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 2,
++ JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 0, 2,
+ JH7110_AONCLK_OSC_DIV4,
+ JH7110_AONCLK_OSC),
+ /* gmac0 */
+@@ -39,7 +39,7 @@ static const struct jh71x0_clk_data jh71
+ JH7110_AONCLK_GMAC0_GTXCLK,
+ JH7110_AONCLK_GMAC0_RMII_RTX),
+ JH71X0__INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv", JH7110_AONCLK_GMAC0_TX),
+- JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 2,
++ JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 0, 2,
+ JH7110_AONCLK_GMAC0_RGMII_RXIN,
+ JH7110_AONCLK_GMAC0_RMII_RTX),
+ JH71X0__INV(JH7110_AONCLK_GMAC0_RX_INV, "gmac0_rx_inv", JH7110_AONCLK_GMAC0_RX),
+@@ -48,7 +48,7 @@ static const struct jh71x0_clk_data jh71
+ /* rtc */
+ JH71X0_GATE(JH7110_AONCLK_RTC_APB, "rtc_apb", 0, JH7110_AONCLK_APB_BUS),
+ JH71X0__DIV(JH7110_AONCLK_RTC_INTERNAL, "rtc_internal", 1022, JH7110_AONCLK_OSC),
+- JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 2,
++ JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 0, 2,
+ JH7110_AONCLK_RTC_OSC,
+ JH7110_AONCLK_RTC_INTERNAL),
+ JH71X0_GATE(JH7110_AONCLK_RTC_CAL, "rtc_cal", 0, JH7110_AONCLK_OSC),
+--- a/drivers/clk/starfive/clk-starfive-jh7110-isp.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c
+@@ -53,7 +53,7 @@ static const struct jh71x0_clk_data jh71
+ JH7110_ISPCLK_MIPI_RX0_PXL),
+ JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF3, "vin_pixel_if3", 0,
+ JH7110_ISPCLK_MIPI_RX0_PXL),
+- JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 2,
++ JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 0, 2,
+ JH7110_ISPCLK_MIPI_RX0_PXL,
+ JH7110_ISPCLK_DVP_INV),
+ /* ispv2_top_wrapper */
+--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c
+@@ -36,18 +36,18 @@
+
+ static const struct jh71x0_clk_data jh7110_sysclk_data[] __initconst = {
+ /* root */
+- JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 2,
++ JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 0, 2,
+ JH7110_SYSCLK_OSC,
+ JH7110_SYSCLK_PLL0_OUT),
+ JH71X0__DIV(JH7110_SYSCLK_CPU_CORE, "cpu_core", 7, JH7110_SYSCLK_CPU_ROOT),
+ JH71X0__DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", 2, JH7110_SYSCLK_CPU_CORE),
+- JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 2,
++ JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 0, 2,
+ JH7110_SYSCLK_PLL2_OUT,
+ JH7110_SYSCLK_PLL1_OUT),
+ JH71X0_MDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", 2, 2,
+ JH7110_SYSCLK_PLL0_OUT,
+ JH7110_SYSCLK_PLL2_OUT),
+- JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 2,
++ JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 0, 2,
+ JH7110_SYSCLK_OSC,
+ JH7110_SYSCLK_PLL2_OUT),
+ JH71X0__DIV(JH7110_SYSCLK_NOCSTG_BUS, "nocstg_bus", 3, JH7110_SYSCLK_BUS_ROOT),
+@@ -62,7 +62,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0__DIV(JH7110_SYSCLK_PLL2_DIV2, "pll2_div2", 2, JH7110_SYSCLK_PLL2_OUT),
+ JH71X0__DIV(JH7110_SYSCLK_AUDIO_ROOT, "audio_root", 8, JH7110_SYSCLK_PLL2_OUT),
+ JH71X0__DIV(JH7110_SYSCLK_MCLK_INNER, "mclk_inner", 64, JH7110_SYSCLK_AUDIO_ROOT),
+- JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 2,
++ JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 0, 2,
+ JH7110_SYSCLK_MCLK_INNER,
+ JH7110_SYSCLK_MCLK_EXT),
+ JH71X0_GATE(JH7110_SYSCLK_MCLK_OUT, "mclk_out", 0, JH7110_SYSCLK_MCLK_INNER),
+@@ -96,7 +96,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0__DIV(JH7110_SYSCLK_OSC_DIV2, "osc_div2", 2, JH7110_SYSCLK_OSC),
+ JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV4, "pll1_div4", 2, JH7110_SYSCLK_PLL1_DIV2),
+ JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV8, "pll1_div8", 2, JH7110_SYSCLK_PLL1_DIV4),
+- JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 4,
++ JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 0, 4,
+ JH7110_SYSCLK_OSC_DIV2,
+ JH7110_SYSCLK_PLL1_DIV2,
+ JH7110_SYSCLK_PLL1_DIV4,
+@@ -186,7 +186,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0__DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx", 30,
+ JH7110_SYSCLK_GMAC1_RMII_REFIN),
+ JH71X0_GDIV(JH7110_SYSCLK_GMAC1_PTP, "gmac1_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC),
+- JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 2,
++ JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 0, 2,
+ JH7110_SYSCLK_GMAC1_RGMII_RXIN,
+ JH7110_SYSCLK_GMAC1_RMII_RTX),
+ JH71X0__INV(JH7110_SYSCLK_GMAC1_RX_INV, "gmac1_rx_inv", JH7110_SYSCLK_GMAC1_RX),
+@@ -270,11 +270,11 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_MDIV(JH7110_SYSCLK_I2STX0_LRCK_MST, "i2stx0_lrck_mst", 64, 2,
+ JH7110_SYSCLK_I2STX0_BCLK_MST_INV,
+ JH7110_SYSCLK_I2STX0_BCLK_MST),
+- JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk", 0, 2,
+ JH7110_SYSCLK_I2STX0_BCLK_MST,
+ JH7110_SYSCLK_I2STX_BCLK_EXT),
+ JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_INV, "i2stx0_bclk_inv", JH7110_SYSCLK_I2STX0_BCLK),
+- JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 0, 2,
+ JH7110_SYSCLK_I2STX0_LRCK_MST,
+ JH7110_SYSCLK_I2STX_LRCK_EXT),
+ /* i2stx1 */
+@@ -285,11 +285,11 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_MDIV(JH7110_SYSCLK_I2STX1_LRCK_MST, "i2stx1_lrck_mst", 64, 2,
+ JH7110_SYSCLK_I2STX1_BCLK_MST_INV,
+ JH7110_SYSCLK_I2STX1_BCLK_MST),
+- JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 0, 2,
+ JH7110_SYSCLK_I2STX1_BCLK_MST,
+ JH7110_SYSCLK_I2STX_BCLK_EXT),
+ JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_INV, "i2stx1_bclk_inv", JH7110_SYSCLK_I2STX1_BCLK),
+- JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 0, 2,
+ JH7110_SYSCLK_I2STX1_LRCK_MST,
+ JH7110_SYSCLK_I2STX_LRCK_EXT),
+ /* i2srx */
+@@ -300,11 +300,11 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_MDIV(JH7110_SYSCLK_I2SRX_LRCK_MST, "i2srx_lrck_mst", 64, 2,
+ JH7110_SYSCLK_I2SRX_BCLK_MST_INV,
+ JH7110_SYSCLK_I2SRX_BCLK_MST),
+- JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 0, 2,
+ JH7110_SYSCLK_I2SRX_BCLK_MST,
+ JH7110_SYSCLK_I2SRX_BCLK_EXT),
+ JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_INV, "i2srx_bclk_inv", JH7110_SYSCLK_I2SRX_BCLK),
+- JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 2,
++ JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 0, 2,
+ JH7110_SYSCLK_I2SRX_LRCK_MST,
+ JH7110_SYSCLK_I2SRX_LRCK_EXT),
+ /* pdm */
+@@ -314,7 +314,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GATE(JH7110_SYSCLK_TDM_AHB, "tdm_ahb", 0, JH7110_SYSCLK_AHB0),
+ JH71X0_GATE(JH7110_SYSCLK_TDM_APB, "tdm_apb", 0, JH7110_SYSCLK_APB0),
+ JH71X0_GDIV(JH7110_SYSCLK_TDM_INTERNAL, "tdm_internal", 0, 64, JH7110_SYSCLK_MCLK),
+- JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 2,
++ JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 0, 2,
+ JH7110_SYSCLK_TDM_INTERNAL,
+ JH7110_SYSCLK_TDM_EXT),
+ JH71X0__INV(JH7110_SYSCLK_TDM_TDM_INV, "tdm_tdm_inv", JH7110_SYSCLK_TDM_TDM),
+--- a/drivers/clk/starfive/clk-starfive-jh71x0.h
++++ b/drivers/clk/starfive/clk-starfive-jh71x0.h
+@@ -61,10 +61,10 @@ struct jh71x0_clk_data {
+ .parents = { [0] = _parent }, \
+ }
+
+-#define JH71X0__MUX(_idx, _name, _nparents, ...) \
++#define JH71X0__MUX(_idx, _name, _flags, _nparents, ...) \
+ [_idx] = { \
+ .name = _name, \
+- .flags = 0, \
++ .flags = _flags, \
+ .max = ((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT, \
+ .parents = { __VA_ARGS__ }, \
+ }
--- /dev/null
+From 7dfed4b67bd6ba8c33caf90c01821b67cf3260dd Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Sat, 25 Mar 2023 23:04:31 +0100
+Subject: [PATCH 1007/1024] clk: starfive: jh7100: Add CLK_SET_RATE_PARENT to
+ gmac_tx
+
+This is needed by the dwmac-starfive ethernet driver to set the clock
+for 1000, 100 and 10 Mbps links properly.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+---
+ drivers/clk/starfive/clk-starfive-jh7100.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -200,7 +200,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+- JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 0, 3,
++ JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 3,
+ JH7100_CLK_GMAC_GTX,
+ JH7100_CLK_GMAC_TX_INV,
+ JH7100_CLK_GMAC_RMII_TX),
--- /dev/null
+From f8d08ec17674e56c7c689f5ca43c4dd3a4b76d13 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 14 Oct 2021 20:35:43 +0200
+Subject: [PATCH 1008/1024] clk: starfive: jh7100: Keep more clocks alive
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/clk/starfive/clk-starfive-jh7100.c | 37 +++++++++++-----------
+ 1 file changed, 19 insertions(+), 18 deletions(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -94,9 +94,9 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI),
+ JH71X0_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS),
+ JH71X0__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT),
+- JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS),
+- JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS),
+- JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS),
++ JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS),
++ JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS),
++ JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS),
+ JH71X0_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV),
+ JH71X0__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT),
+ JH71X0_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC),
+@@ -160,11 +160,12 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS),
+ JH71X0_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+ JH71X0__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+- JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS),
+- JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS),
++ JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS),
++ JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS),
+ JH71X0__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT),
+- JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV),
+- JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32,
++ JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", CLK_IGNORE_UNUSED, 8,
++ JH7100_CLK_USBPHY_ROOTDIV),
++ JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", CLK_IGNORE_UNUSED, 32,
+ JH7100_CLK_USBPHY_ROOTDIV),
+ JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 0, 2,
+ JH7100_CLK_OSC_SYS,
+@@ -183,23 +184,23 @@ static const struct jh71x0_clk_data jh71
+ JH71X0__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC),
+ JH71X0_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS),
+ JH71X0_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS),
+- JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT),
++ JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", CLK_IGNORE_UNUSED, 4, JH7100_CLK_VOUT_ROOT),
+ JH71X0__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT),
+ JH71X0__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC),
+- JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS),
+- JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS),
++ JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS),
++ JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS),
+ JH71X0_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS),
+ JH71X0_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC),
+ JH71X0__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT),
+ JH71X0_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS),
+ JH71X0_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC),
+ JH71X0__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT),
+- JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS),
++ JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", CLK_IGNORE_UNUSED, JH7100_CLK_AHB_BUS),
+ JH71X0__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT),
+- JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV),
+- JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+- JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+- JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
++ JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", CLK_IGNORE_UNUSED, 31, JH7100_CLK_GMAC_ROOT_DIV),
++ JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", CLK_IGNORE_UNUSED, 255, JH7100_CLK_GMAC_ROOT_DIV),
++ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF),
++ JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF),
+ JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 3,
+ JH7100_CLK_GMAC_GTX,
+ JH7100_CLK_GMAC_TX_INV,
+@@ -209,8 +210,8 @@ static const struct jh71x0_clk_data jh71
+ JH7100_CLK_GMAC_GR_MII_RX,
+ JH7100_CLK_GMAC_RMII_RX),
+ JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE),
+- JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF),
+- JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV),
++ JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", CLK_IGNORE_UNUSED, JH7100_CLK_GMAC_RMII_REF),
++ JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", CLK_IGNORE_UNUSED, 127, JH7100_CLK_GMAC_ROOT_DIV),
+ JH71X0_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS),
+ JH71X0_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC),
+ JH71X0_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS),
+@@ -223,7 +224,7 @@ static const struct jh71x0_clk_data jh71
+ JH71X0_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB),
+ JH71X0_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB),
+ JH71X0_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB),
+- JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS),
++ JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS),
+ JH71X0_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS),
+ JH71X0_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS),
+ JH71X0_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC),
--- /dev/null
+From 980f1e9ef19d472e72c36e142db7fb4e224f0f3e Mon Sep 17 00:00:00 2001
+From: Matteo Croce <technoboy85@gmail.com>
+Date: Fri, 21 May 2021 03:26:38 +0200
+Subject: [PATCH 1009/1024] net: stmmac: use GFP_DMA32
+
+Signed-off-by: Matteo Croce <mcroce@microsoft.com>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -1434,7 +1434,7 @@ static int stmmac_init_rx_buffers(struct
+ {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+- gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
++ gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+
+ if (priv->dma_cap.host_dma_width <= 32)
+ gfp |= GFP_DMA32;
+@@ -4673,7 +4673,7 @@ static inline void stmmac_rx_refill(stru
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int dirty = stmmac_rx_dirty(priv, queue);
+ unsigned int entry = rx_q->dirty_rx;
+- gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
++ gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+
+ if (priv->dma_cap.host_dma_width <= 32)
+ gfp |= GFP_DMA32;
--- /dev/null
+From 4989e7aa5ed5ef9bc2532b3a47ff381572f389b5 Mon Sep 17 00:00:00 2001
+From: Huan Feng <huan.feng@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:35:42 +0800
+Subject: [PATCH 1010/1024] hwrng: Add StarFive JH7100 Random Number Generator
+ driver
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/char/hw_random/Kconfig | 13 ++
+ drivers/char/hw_random/Makefile | 1 +
+ drivers/char/hw_random/starfive-vic-rng.c | 256 ++++++++++++++++++++++
+ drivers/char/hw_random/starfive-vic-rng.h | 167 ++++++++++++++
+ 4 files changed, 437 insertions(+)
+ create mode 100644 drivers/char/hw_random/starfive-vic-rng.c
+ create mode 100644 drivers/char/hw_random/starfive-vic-rng.h
+
+--- a/drivers/char/hw_random/Kconfig
++++ b/drivers/char/hw_random/Kconfig
+@@ -322,6 +322,19 @@ config HW_RANDOM_POWERNV
+
+ If unsure, say Y.
+
++config HW_RANDOM_STARFIVE_VIC
++ tristate "Starfive VIC Random Number Generator support"
++ depends on HW_RANDOM && (SOC_STARFIVE || COMPILE_TEST)
++ default SOC_STARFIVE
++ help
++ This driver provides kernel-side support for the Random Number
++ Generator hardware found on Starfive VIC SoC.
++
++ To compile this driver as a module, choose M here: the
++ module will be called starfive-vic-rng.
++
++ If unsure, say Y.
++
+ config HW_RANDOM_HISI
+ tristate "Hisilicon Random Number Generator support"
+ depends on ARCH_HISI || COMPILE_TEST
+--- a/drivers/char/hw_random/Makefile
++++ b/drivers/char/hw_random/Makefile
+@@ -28,6 +28,7 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon
+ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
+ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
+ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
++obj-$(CONFIG_HW_RANDOM_STARFIVE_VIC) += starfive-vic-rng.o
+ obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o
+ obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o
+ obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+--- /dev/null
++++ b/drivers/char/hw_random/starfive-vic-rng.c
+@@ -0,0 +1,256 @@
++/*
++ ******************************************************************************
++ * @file starfive-vic-rng.c
++ * @author StarFive Technology
++ * @version V1.0
++ * @date 08/13/2020
++ * @brief
++ ******************************************************************************
++ * @copy
++ *
++ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
++ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
++ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
++ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
++ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
++ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++ *
++ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
++ */
++#include <linux/err.h>
++#include <linux/kernel.h>
++#include <linux/hw_random.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/random.h>
++
++#include "starfive-vic-rng.h"
++
++#define to_vic_rng(p) container_of(p, struct vic_rng, rng)
++
++struct vic_rng {
++ struct device *dev;
++ void __iomem *base;
++ struct hwrng rng;
++};
++
++static inline void vic_wait_till_idle(struct vic_rng *hrng)
++{
++ while(readl(hrng->base + VIC_STAT) & VIC_STAT_BUSY)
++ ;
++}
++
++static inline void vic_rng_irq_mask_clear(struct vic_rng *hrng)
++{
++ // clear register: ISTAT
++ u32 data = readl(hrng->base + VIC_ISTAT);
++ writel(data, hrng->base + VIC_ISTAT);
++ writel(0, hrng->base + VIC_ALARM);
++}
++
++static int vic_trng_cmd(struct vic_rng *hrng, u32 cmd) {
++ int res = 0;
++ // wait till idle
++ vic_wait_till_idle(hrng);
++ switch (cmd) {
++ case VIC_CTRL_CMD_NOP:
++ case VIC_CTRL_CMD_GEN_NOISE:
++ case VIC_CTRL_CMD_GEN_NONCE:
++ case VIC_CTRL_CMD_CREATE_STATE:
++ case VIC_CTRL_CMD_RENEW_STATE:
++ case VIC_CTRL_CMD_REFRESH_ADDIN:
++ case VIC_CTRL_CMD_GEN_RANDOM:
++ case VIC_CTRL_CMD_ADVANCE_STATE:
++ case VIC_CTRL_CMD_KAT:
++ case VIC_CTRL_CMD_ZEROIZE:
++ writel(cmd, hrng->base + VIC_CTRL);
++ break;
++ default:
++ res = -1;
++ break;
++ }
++
++ return res;
++}
++
++static int vic_rng_init(struct hwrng *rng)
++{
++ struct vic_rng *hrng = to_vic_rng(rng);
++
++ // wait till idle
++
++ // clear register: ISTAT
++ vic_rng_irq_mask_clear(hrng);
++
++ // set mission mode
++ writel(VIC_SMODE_SECURE_EN(1), hrng->base + VIC_SMODE);
++
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE);
++ vic_wait_till_idle(hrng);
++
++ // set interrupt
++ writel(VIC_IE_ALL, hrng->base + VIC_IE);
++
++ // zeroize
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++
++ vic_wait_till_idle(hrng);
++
++ return 0;
++}
++
++static irqreturn_t vic_rng_irq(int irq, void *priv)
++{
++ u32 status, val;
++ struct vic_rng *hrng = (struct vic_rng *)priv;
++
++ /*
++ * clearing the interrupt will also clear the error register
++ * read error and status before clearing
++ */
++ status = readl(hrng->base + VIC_ISTAT);
++
++ if (status & VIC_ISTAT_ALARMS) {
++ writel(VIC_ISTAT_ALARMS, hrng->base + VIC_ISTAT);
++ val = readl(hrng->base + VIC_ALARM);
++ if (val & VIC_ALARM_ILLEGAL_CMD_SEQ) {
++ writel(VIC_ALARM_ILLEGAL_CMD_SEQ, hrng->base + VIC_ALARM);
++ //dev_info(hrng->dev, "ILLEGAL CMD SEQ: LAST_CMD=0x%x\r\n",
++ //VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT)));
++ } else {
++ dev_info(hrng->dev, "Failed test: %x\r\n", val);
++ }
++ }
++
++ if (status & VIC_ISTAT_ZEROIZE) {
++ writel(VIC_ISTAT_ZEROIZE, hrng->base + VIC_ISTAT);
++ //dev_info(hrng->dev, "zeroized\r\n");
++ }
++
++ if (status & VIC_ISTAT_KAT_COMPLETE) {
++ writel(VIC_ISTAT_KAT_COMPLETE, hrng->base + VIC_ISTAT);
++ //dev_info(hrng->dev, "kat_completed\r\n");
++ }
++
++ if (status & VIC_ISTAT_NOISE_RDY) {
++ writel(VIC_ISTAT_NOISE_RDY, hrng->base + VIC_ISTAT);
++ //dev_info(hrng->dev, "noise_rdy\r\n");
++ }
++
++ if (status & VIC_ISTAT_DONE) {
++ writel(VIC_ISTAT_DONE, hrng->base + VIC_ISTAT);
++ //dev_info(hrng->dev, "done\r\n");
++ /*
++ if (VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT)) ==
++ VIC_CTRL_CMD_GEN_RANDOM) {
++ dev_info(hrng->dev, "Need Update Buffer\r\n");
++ }
++ */
++ }
++ vic_rng_irq_mask_clear(hrng);
++
++ return IRQ_HANDLED;
++}
++
++static void vic_rng_cleanup(struct hwrng *rng)
++{
++ struct vic_rng *hrng = to_vic_rng(rng);
++
++ writel(0, hrng->base + VIC_CTRL);
++}
++
++static int vic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
++{
++ struct vic_rng *hrng = to_vic_rng(rng);
++
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE);
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_CREATE_STATE);
++
++ vic_wait_till_idle(hrng);
++ max = min_t(size_t, max, (VIC_RAND_LEN * 4));
++
++ writel(0x0, hrng->base + VIC_MODE);
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_RANDOM);
++
++ vic_wait_till_idle(hrng);
++ memcpy_fromio(buf, hrng->base + VIC_RAND0, max);
++ vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++
++ vic_wait_till_idle(hrng);
++ return max;
++}
++
++static int vic_rng_probe(struct platform_device *pdev)
++{
++ int ret;
++ int irq;
++ struct vic_rng *rng;
++ struct resource *res;
++
++ rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
++ if (!rng){
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev, rng);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ rng->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rng->base)){
++ return PTR_ERR(rng->base);
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq <= 0) {
++ dev_err(&pdev->dev, "Couldn't get irq %d\n", irq);
++ return irq;
++ }
++
++ ret = devm_request_irq(&pdev->dev, irq, vic_rng_irq, 0, pdev->name,
++ (void *)rng);
++ if (ret) {
++ dev_err(&pdev->dev, "Can't get interrupt working.\n");
++ return ret;
++ }
++
++ rng->rng.name = pdev->name;
++ rng->rng.init = vic_rng_init;
++ rng->rng.cleanup = vic_rng_cleanup;
++ rng->rng.read = vic_rng_read;
++
++ rng->dev = &pdev->dev;
++
++ ret = devm_hwrng_register(&pdev->dev, &rng->rng);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to register hwrng\n");
++ return ret;
++ }
++
++ dev_info(&pdev->dev, "Initialized\n");
++
++ return 0;
++}
++
++static const struct of_device_id vic_rng_dt_ids[] = {
++ { .compatible = "starfive,vic-rng" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, vic_rng_dt_ids);
++
++static struct platform_driver vic_rng_driver = {
++ .probe = vic_rng_probe,
++ .driver = {
++ .name = "vic-rng",
++ .of_match_table = vic_rng_dt_ids,
++ },
++};
++
++module_platform_driver(vic_rng_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>");
++MODULE_DESCRIPTION("Starfive VIC random number generator driver");
+--- /dev/null
++++ b/drivers/char/hw_random/starfive-vic-rng.h
+@@ -0,0 +1,167 @@
++/*
++ ******************************************************************************
++ * @file starfive-vic-rng.h
++ * @author StarFive Technology
++ * @version V1.0
++ * @date 08/13/2020
++ * @brief
++ ******************************************************************************
++ * @copy
++ *
++ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
++ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
++ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
++ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
++ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
++ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++ *
++ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
++ */
++
++#define VIC_CTRL 0x00
++#define VIC_MODE 0x04
++#define VIC_SMODE 0x08
++#define VIC_STAT 0x0C
++#define VIC_IE 0x10
++#define VIC_ISTAT 0x14
++#define VIC_ALARM 0x18
++#define VIC_BUILD_ID 0x1C
++#define VIC_FEATURES 0x20
++#define VIC_RAND0 0x24
++#define VIC_NPA_DATA0 0x34
++#define VIC_SEED0 0x74
++#define VIC_IA_RDATA 0xA4
++#define VIC_IA_WDATA 0xA8
++#define VIC_IA_ADDR 0xAC
++#define VIC_IA_CMD 0xB0
++
++/* CTRL */
++#define VIC_CTRL_CMD_NOP 0
++#define VIC_CTRL_CMD_GEN_NOISE 1
++#define VIC_CTRL_CMD_GEN_NONCE 2
++#define VIC_CTRL_CMD_CREATE_STATE 3
++#define VIC_CTRL_CMD_RENEW_STATE 4
++#define VIC_CTRL_CMD_REFRESH_ADDIN 5
++#define VIC_CTRL_CMD_GEN_RANDOM 6
++#define VIC_CTRL_CMD_ADVANCE_STATE 7
++#define VIC_CTRL_CMD_KAT 8
++#define VIC_CTRL_CMD_ZEROIZE 15
++
++/* MODE */
++#define _VIC_MODE_ADDIN_PRESENT 4
++#define _VIC_MODE_PRED_RESIST 3
++#define _VIC_MODE_KAT_SEL 2
++#define _VIC_MODE_KAT_VEC 1
++#define _VIC_MODE_SEC_ALG 0
++
++#define VIC_MODE_ADDIN_PRESENT (1UL << _VIC_MODE_ADDIN_PRESENT)
++#define VIC_MODE_PRED_RESIST (1UL << _VIC_MODE_PRED_RESIST)
++#define VIC_MODE_KAT_SEL (1UL << _VIC_MODE_KAT_SEL)
++#define VIC_MODE_KAT_VEC (1UL << _VIC_MODE_KAT_VEC)
++#define VIC_MODE_SEC_ALG (1UL << _VIC_MODE_SEC_ALG)
++
++/* SMODE */
++#define _VIC_SMODE_MAX_REJECTS 2
++#define _VIC_SMODE_SECURE_EN 1
++#define _VIC_SMODE_NONCE 0
++
++#define VIC_SMODE_MAX_REJECTS(x) ((x) << _VIC_SMODE_MAX_REJECTS)
++#define VIC_SMODE_SECURE_EN(x) ((x) << _VIC_SMODE_SECURE_EN)
++#define VIC_SMODE_NONCE (1UL << _VIC_SMODE_NONCE)
++
++/* STAT */
++#define _VIC_STAT_BUSY 31
++#define _VIC_STAT_DRBG_STATE 7
++#define _VIC_STAT_SECURE 6
++#define _VIC_STAT_NONCE_MODE 5
++#define _VIC_STAT_SEC_ALG 4
++#define _VIC_STAT_LAST_CMD 0
++
++#define VIC_STAT_BUSY (1UL << _VIC_STAT_BUSY)
++#define VIC_STAT_DRBG_STATE (1UL << _VIC_STAT_DRBG_STATE)
++#define VIC_STAT_SECURE (1UL << _VIC_STAT_SECURE)
++#define VIC_STAT_NONCE_MODE (1UL << _VIC_STAT_NONCE_MODE)
++#define VIC_STAT_SEC_ALG (1UL << _VIC_STAT_SEC_ALG)
++#define VIC_STAT_LAST_CMD(x) (((x) >> _VIC_STAT_LAST_CMD) & 0xF)
++
++/* IE */
++#define _VIC_IE_GLBL 31
++#define _VIC_IE_DONE 4
++#define _VIC_IE_ALARMS 3
++#define _VIC_IE_NOISE_RDY 2
++#define _VIC_IE_KAT_COMPLETE 1
++#define _VIC_IE_ZEROIZE 0
++
++#define VIC_IE_GLBL (1UL << _VIC_IE_GLBL)
++#define VIC_IE_DONE (1UL << _VIC_IE_DONE)
++#define VIC_IE_ALARMS (1UL << _VIC_IE_ALARMS)
++#define VIC_IE_NOISE_RDY (1UL << _VIC_IE_NOISE_RDY)
++#define VIC_IE_KAT_COMPLETE (1UL << _VIC_IE_KAT_COMPLETE)
++#define VIC_IE_ZEROIZE (1UL << _VIC_IE_ZEROIZE)
++#define VIC_IE_ALL (VIC_IE_GLBL | VIC_IE_DONE | VIC_IE_ALARMS | \
++ VIC_IE_NOISE_RDY | VIC_IE_KAT_COMPLETE | VIC_IE_ZEROIZE)
++
++/* ISTAT */
++#define _VIC_ISTAT_DONE 4
++#define _VIC_ISTAT_ALARMS 3
++#define _VIC_ISTAT_NOISE_RDY 2
++#define _VIC_ISTAT_KAT_COMPLETE 1
++#define _VIC_ISTAT_ZEROIZE 0
++
++#define VIC_ISTAT_DONE (1UL << _VIC_ISTAT_DONE)
++#define VIC_ISTAT_ALARMS (1UL << _VIC_ISTAT_ALARMS)
++#define VIC_ISTAT_NOISE_RDY (1UL << _VIC_ISTAT_NOISE_RDY)
++#define VIC_ISTAT_KAT_COMPLETE (1UL << _VIC_ISTAT_KAT_COMPLETE)
++#define VIC_ISTAT_ZEROIZE (1UL << _VIC_ISTAT_ZEROIZE)
++
++/* ALARMS */
++#define VIC_ALARM_ILLEGAL_CMD_SEQ (1UL << 4)
++#define VIC_ALARM_FAILED_TEST_ID_OK 0
++#define VIC_ALARM_FAILED_TEST_ID_KAT_STAT 1
++#define VIC_ALARM_FAILED_TEST_ID_KAT 2
++#define VIC_ALARM_FAILED_TEST_ID_MONOBIT 3
++#define VIC_ALARM_FAILED_TEST_ID_RUN 4
++#define VIC_ALARM_FAILED_TEST_ID_LONGRUN 5
++#define VIC_ALARM_FAILED_TEST_ID_AUTOCORRELATION 6
++#define VIC_ALARM_FAILED_TEST_ID_POKER 7
++#define VIC_ALARM_FAILED_TEST_ID_REPETITION_COUNT 8
++#define VIC_ALARM_FAILED_TEST_ID_ADAPATIVE_PROPORTION 9
++
++/* BUILD_ID */
++#define VIC_BUILD_ID_STEPPING(x) (((x) >> 28) & 0xF)
++#define VIC_BUILD_ID_EPN(x) ((x) & 0xFFFF)
++
++/* FEATURES */
++#define VIC_FEATURES_AES_256(x) (((x) >> 9) & 1)
++#define VIC_FEATURES_EXTRA_PS_PRESENT(x) (((x) >> 8) & 1)
++#define VIC_FEATURES_DIAG_LEVEL_NS(x) (((x) >> 7) & 1)
++#define VIC_FEATURES_DIAG_LEVEL_CLP800(x) (((x) >> 4) & 7)
++#define VIC_FEATURES_DIAG_LEVEL_ST_HLT(x) (((x) >> 1) & 7)
++#define VIC_FEATURES_SECURE_RST_STATE(x) ((x) & 1)
++
++/* IA_CMD */
++#define VIC_IA_CMD_GO (1UL << 31)
++#define VIC_IA_CMD_WR (1)
++
++#define _VIC_SMODE_MAX_REJECTS_MASK 255UL
++#define _VIC_SMODE_SECURE_EN_MASK 1UL
++#define _VIC_SMODE_NONCE_MASK 1UL
++#define _VIC_MODE_SEC_ALG_MASK 1UL
++#define _VIC_MODE_ADDIN_PRESENT_MASK 1UL
++#define _VIC_MODE_PRED_RESIST_MASK 1UL
++
++#define VIC_SMODE_SET_MAX_REJECTS(y, x) (((y) & ~(_VIC_SMODE_MAX_REJECTS_MASK << _VIC_SMODE_MAX_REJECTS)) | ((x) << _VIC_SMODE_MAX_REJECTS))
++#define VIC_SMODE_SET_SECURE_EN(y, x) (((y) & ~(_VIC_SMODE_SECURE_EN_MASK << _VIC_SMODE_SECURE_EN)) | ((x) << _VIC_SMODE_SECURE_EN))
++#define VIC_SMODE_SET_NONCE(y, x) (((y) & ~(_VIC_SMODE_NONCE_MASK << _VIC_SMODE_NONCE)) | ((x) << _VIC_SMODE_NONCE))
++#define VIC_SMODE_GET_MAX_REJECTS(x) (((x) >> _VIC_SMODE_MAX_REJECTS) & _VIC_SMODE_MAX_REJECTS_MASK)
++#define VIC_SMODE_GET_SECURE_EN(x) (((x) >> _VIC_SMODE_SECURE_EN) & _VIC_SMODE_SECURE_EN_MASK)
++#define VIC_SMODE_GET_NONCE(x) (((x) >> _VIC_SMODE_NONCE) & _VIC_SMODE_NONCE_MASK)
++
++#define VIC_MODE_SET_SEC_ALG(y, x) (((y) & ~(_VIC_MODE_SEC_ALG_MASK << _VIC_MODE_SEC_ALG)) | ((x) << _VIC_MODE_SEC_ALG))
++#define VIC_MODE_SET_PRED_RESIST(y, x) (((y) & ~(_VIC_MODE_PRED_RESIST_MASK << _VIC_MODE_PRED_RESIST)) | ((x) << _VIC_MODE_PRED_RESIST))
++#define VIC_MODE_SET_ADDIN_PRESENT(y, x) (((y) & ~(_VIC_MODE_ADDIN_PRESENT_MASK << _VIC_MODE_ADDIN_PRESENT)) | ((x) << _VIC_MODE_ADDIN_PRESENT))
++#define VIC_MODE_GET_SEC_ALG(x) (((x) >> _VIC_MODE_SEC_ALG) & _VIC_MODE_SEC_ALG_MASK)
++#define VIC_MODE_GET_PRED_RESIST(x) (((x) >> _VIC_MODE_PRED_RESIST) & _VIC_MODE_PRED_RESIST_MASK)
++#define VIC_MODE_GET_ADDIN_PRESENT(x) (((x) >> _VIC_MODE_ADDIN_PRESENT) & _VIC_MODE_ADDIN_PRESENT_MASK)
++
++#define VIC_RAND_LEN 4
--- /dev/null
+From 34fd7b7f92deef97f03c80d056e2f51bc08b7dc6 Mon Sep 17 00:00:00 2001
+From: Chenjieqin <Jessica.Chen@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:56:54 +0800
+Subject: [PATCH 1011/1024] pwm: sifive-ptc: Add SiFive PWM PTC driver
+
+yiming.li: clear CNTR of PWM after setting period & duty_cycle
+Emil: cleanups, clock, reset and div_u64
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/pwm/Kconfig | 11 ++
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-sifive-ptc.c | 260 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 272 insertions(+)
+ create mode 100644 drivers/pwm/pwm-sifive-ptc.c
+
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -549,6 +549,17 @@ config PWM_SIFIVE
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sifive.
+
++config PWM_SIFIVE_PTC
++ tristate "SiFive PWM PTC support"
++ depends on SOC_SIFIVE || SOC_STARFIVE || COMPILE_TEST
++ depends on OF
++ depends on COMMON_CLK
++ help
++ Generic PWM framework driver for SiFive SoCs.
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-sifive-ptc.
++
+ config PWM_SL28CPLD
+ tristate "Kontron sl28cpld PWM support"
+ depends on MFD_SL28CPLD || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -50,6 +50,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockch
+ obj-$(CONFIG_PWM_RZ_MTU3) += pwm-rz-mtu3.o
+ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
+ obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
++obj-$(CONFIG_PWM_SIFIVE_PTC) += pwm-sifive-ptc.o
+ obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
+ obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
+ obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o
+--- /dev/null
++++ b/drivers/pwm/pwm-sifive-ptc.c
+@@ -0,0 +1,260 @@
++/*
++ * Copyright (C) 2018 SiFive, Inc
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2, as published by
++ * the Free Software Foundation.
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/reset.h>
++
++#include <dt-bindings/pwm/pwm.h>
++
++/* max channel of pwm */
++#define MAX_PWM 8
++
++/* PTC Register offsets */
++#define REG_RPTC_CNTR 0x0
++#define REG_RPTC_HRC 0x4
++#define REG_RPTC_LRC 0x8
++#define REG_RPTC_CTRL 0xC
++
++/* Bit for PWM clock */
++#define BIT_PWM_CLOCK_EN 31
++
++/* Bit for clock gen soft reset */
++#define BIT_CLK_GEN_SOFT_RESET 13
++
++#define NS_1 1000000000U
++
++/* Access PTC register (cntr hrc lrc and ctrl), need to replace PWM_BASE_ADDR */
++#define REG_PTC_BASE_ADDR_SUB(base, N) \
++ ((base) + (((N) > 3) ? (((N) - 4) * 0x10 + (1 << 15)) : ((N) * 0x10)))
++#define REG_PTC_RPTC_CNTR(base, N) (REG_PTC_BASE_ADDR_SUB(base, N))
++#define REG_PTC_RPTC_HRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4)
++#define REG_PTC_RPTC_LRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8)
++#define REG_PTC_RPTC_CTRL(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC)
++
++/* pwm ptc device */
++struct sifive_pwm_ptc_device {
++ struct pwm_chip chip;
++ struct clk *clk;
++ void __iomem *regs;
++};
++
++static inline struct sifive_pwm_ptc_device *chip_to_sifive_ptc(struct pwm_chip *c)
++{
++ return container_of(c, struct sifive_pwm_ptc_device, chip);
++}
++
++static int sifive_pwm_ptc_get_state(struct pwm_chip *chip, struct pwm_device *dev,
++ struct pwm_state *state)
++{
++ struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip);
++ u32 data_lrc;
++ u32 data_hrc;
++ u32 pwm_clk_ns = 0;
++
++ /* get lrc and hrc data from registe */
++ data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm));
++ data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm));
++
++ /* how many ns does apb clock elapse */
++ pwm_clk_ns = NS_1 / clk_get_rate(pwm->clk);
++
++ /* pwm period(ns) */
++ state->period = data_lrc * pwm_clk_ns;
++
++ /* duty cycle(ns) means high level eclapse ns if it is normal polarity */
++ state->duty_cycle = data_hrc * pwm_clk_ns;
++
++ /* polarity, we don't use it now because it is not in dts */
++ state->polarity = PWM_POLARITY_NORMAL;
++
++ /* enabled or not */
++ state->enabled = 1;
++
++ dev_dbg(pwm->chip.dev, "%s: no:%d\n", __func__, dev->hwpwm);
++ dev_dbg(pwm->chip.dev, "data_hrc:0x%x 0x%x\n", data_hrc, data_lrc);
++ dev_dbg(pwm->chip.dev, "period:%llu\n", state->period);
++ dev_dbg(pwm->chip.dev, "duty_cycle:%llu\n", state->duty_cycle);
++ dev_dbg(pwm->chip.dev, "polarity:%d\n", state->polarity);
++ dev_dbg(pwm->chip.dev, "enabled:%d\n", state->enabled);
++
++ return 0;
++}
++
++static int sifive_pwm_ptc_apply(struct pwm_chip *chip, struct pwm_device *dev,
++ const struct pwm_state *state)
++{
++ struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip);
++ void __iomem *reg_addr;
++ u32 pwm_clk_ns = 0;
++ u32 data_hrc = 0;
++ u32 data_lrc = 0;
++ u32 period_data = 0;
++ u32 duty_data = 0;
++
++ dev_dbg(pwm->chip.dev, "%s: no:%d\n", __func__, dev->hwpwm);
++ dev_dbg(pwm->chip.dev, "period:%llu\n", state->period);
++ dev_dbg(pwm->chip.dev, "duty_cycle:%llu\n", state->duty_cycle);
++ dev_dbg(pwm->chip.dev, "polarity:%d\n", state->polarity);
++ dev_dbg(pwm->chip.dev, "enabled:%d\n", state->enabled);
++
++ /* duty_cycle should be less or equal than period */
++ if (state->duty_cycle > state->period)
++ return -EINVAL;
++
++ /* calculate pwm real period (ns) */
++ pwm_clk_ns = NS_1 / clk_get_rate(pwm->clk);
++
++ dev_dbg(pwm->chip.dev, "pwm_clk_ns:%u\n", pwm_clk_ns);
++
++ /* calculate period count */
++ period_data = div_u64(state->period, pwm_clk_ns);
++
++ if (!state->enabled)
++ /* if disabled, just set duty_data to 0, which means low level always */
++ duty_data = 0;
++ else
++ /* calculate duty count */
++ duty_data = div_u64(state->duty_cycle, pwm_clk_ns);
++
++ dev_dbg(pwm->chip.dev, "period_data:%u, duty_data:%u\n",
++ period_data, duty_data);
++
++ if (state->polarity == PWM_POLARITY_NORMAL)
++ /* calculate data_hrc */
++ data_hrc = period_data - duty_data;
++ else
++ /* calculate data_hrc */
++ data_hrc = duty_data;
++
++ data_lrc = period_data;
++
++ /* set hrc */
++ reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm);
++ dev_dbg(pwm->chip.dev, "%s: reg_addr:%p, data:%u\n",
++ __func__, reg_addr, data_hrc);
++
++ iowrite32(data_hrc, reg_addr);
++
++ dev_dbg(pwm->chip.dev, "%s: hrc ok\n", __func__);
++
++ /* set lrc */
++ reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm);
++ dev_dbg(pwm->chip.dev, "%s: reg_addr:%p, data:%u\n",
++ __func__, reg_addr, data_lrc);
++
++ iowrite32(data_lrc, reg_addr);
++ dev_dbg(pwm->chip.dev, "%s: lrc ok\n", __func__);
++
++ /* Clear REG_RPTC_CNTR after setting period & duty_cycle */
++ reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm);
++ iowrite32(0, reg_addr);
++ return 0;
++}
++
++static const struct pwm_ops sifive_pwm_ptc_ops = {
++ .get_state = sifive_pwm_ptc_get_state,
++ .apply = sifive_pwm_ptc_apply,
++ .owner = THIS_MODULE,
++};
++
++static void sifive_pwm_ptc_disable_action(void *data)
++{
++ clk_disable_unprepare(data);
++}
++
++static int sifive_pwm_ptc_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *node = pdev->dev.of_node;
++ struct sifive_pwm_ptc_device *pwm;
++ struct pwm_chip *chip;
++ struct reset_control *rst;
++ int ret;
++
++ pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
++ if (!pwm)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, pwm);
++
++ chip = &pwm->chip;
++ chip->dev = dev;
++ chip->ops = &sifive_pwm_ptc_ops;
++
++ /* how many parameters can be transferred to ptc, need to fix */
++ chip->of_pwm_n_cells = 3;
++ chip->base = -1;
++
++ /* get pwm channels count, max value is 8 */
++ ret = of_property_read_u32(node, "starfive,npwm", &chip->npwm);
++ if (ret < 0 || chip->npwm > MAX_PWM)
++ chip->npwm = MAX_PWM;
++
++ dev_dbg(dev, "%s: npwm:0x%x\n", __func__, chip->npwm);
++
++ /* get IO base address */
++ pwm->regs = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(pwm->regs))
++ return dev_err_probe(dev, PTR_ERR(pwm->regs),
++ "Unable to map IO resources\n");
++
++ pwm->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(pwm->clk))
++ return dev_err_probe(dev, PTR_ERR(pwm->clk),
++ "Unable to get controller clock\n");
++
++ ret = clk_prepare_enable(pwm->clk);
++ if (ret)
++ return dev_err_probe(dev, ret, "Unable to enable clock\n");
++
++ ret = devm_add_action_or_reset(dev, sifive_pwm_ptc_disable_action, pwm->clk);
++ if (ret)
++ return ret;
++
++ rst = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(rst))
++ return dev_err_probe(dev, PTR_ERR(rst), "Unable to get reset\n");
++
++ ret = reset_control_deassert(rst);
++ if (ret)
++ return dev_err_probe(dev, ret, "Unable to deassert reset\n");
++
++ /*
++ * after pwmchip_add it will show up as /sys/class/pwm/pwmchip0,
++ * 0 is chip->base, pwm0 can be seen after running echo 0 > export
++ */
++ ret = devm_pwmchip_add(dev, chip);
++ if (ret)
++ return dev_err_probe(dev, ret, "cannot register PTC: %d\n", ret);
++
++ dev_dbg(dev, "SiFive PWM PTC chip registered %d PWMs\n", chip->npwm);
++ return 0;
++}
++
++static const struct of_device_id sifive_pwm_ptc_of_match[] = {
++ { .compatible = "starfive,pwm0" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, sifive_pwm_ptc_of_match);
++
++static struct platform_driver sifive_pwm_ptc_driver = {
++ .probe = sifive_pwm_ptc_probe,
++ .driver = {
++ .name = "pwm-sifive-ptc",
++ .of_match_table = sifive_pwm_ptc_of_match,
++ },
++};
++module_platform_driver(sifive_pwm_ptc_driver);
++
++MODULE_DESCRIPTION("SiFive PWM PTC driver");
++MODULE_LICENSE("GPL v2");
--- /dev/null
+From 4f3335c302b7f944b61f564095505b3c7a1b62ee Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 19:29:25 +0100
+Subject: [PATCH 1012/1024] dt-bindings: reset: Add StarFive JH7100 audio reset
+ definitions
+
+Add all resets for the StarFive JH7100 audio reset controller.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../dt-bindings/reset/starfive-jh7100-audio.h | 31 +++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+ create mode 100644 include/dt-bindings/reset/starfive-jh7100-audio.h
+
+--- /dev/null
++++ b/include/dt-bindings/reset/starfive-jh7100-audio.h
+@@ -0,0 +1,31 @@
++/* SPDX-License-Identifier: GPL-2.0 OR MIT */
++/*
++ * Copyright (C) 2021 Emil Renner Berthing
++ */
++
++#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__
++#define __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__
++
++#define JH7100_AUDRSTN_APB_BUS 0
++#define JH7100_AUDRSTN_I2SADC_APB 1
++#define JH7100_AUDRSTN_I2SADC_SRST 2
++#define JH7100_AUDRSTN_PDM_APB 3
++#define JH7100_AUDRSTN_I2SVAD_APB 4
++#define JH7100_AUDRSTN_I2SVAD_SRST 5
++#define JH7100_AUDRSTN_SPDIF_APB 6
++#define JH7100_AUDRSTN_PWMDAC_APB 7
++#define JH7100_AUDRSTN_I2SDAC_APB 8
++#define JH7100_AUDRSTN_I2SDAC_SRST 9
++#define JH7100_AUDRSTN_I2S1_APB 10
++#define JH7100_AUDRSTN_I2S1_SRST 11
++#define JH7100_AUDRSTN_I2SDAC16K_APB 12
++#define JH7100_AUDRSTN_I2SDAC16K_SRST 13
++#define JH7100_AUDRSTN_DMA1P_AHB 14
++#define JH7100_AUDRSTN_USB_APB 15
++#define JH7100_AUDRST_USB_AXI 16
++#define JH7100_AUDRST_USB_PWRUP_RST_N 17
++#define JH7100_AUDRST_USB_PONRST 18
++
++#define JH7100_AUDRSTN_END 19
++
++#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__ */
--- /dev/null
+From 1e428568e486b40f78febddf2958ba27289bd49f Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Tue, 7 Dec 2021 21:48:51 +0100
+Subject: [PATCH 1013/1024] dt-bindings: reset: Add starfive,jh7100-audrst
+ bindings
+
+Add bindings for the audio reset controller on the StarFive JH7100
+RISC-V SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../reset/starfive,jh7100-audrst.yaml | 38 +++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml
+@@ -0,0 +1,38 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/reset/starfive,jh7100-audrst.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7100 SoC Audio Reset Controller Device Tree Bindings
++
++maintainers:
++ - Emil Renner Berthing <kernel@esmil.dk>
++
++properties:
++ compatible:
++ enum:
++ - starfive,jh7100-audrst
++
++ reg:
++ maxItems: 1
++
++ "#reset-cells":
++ const: 1
++
++required:
++ - compatible
++ - reg
++ - "#reset-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ reset-controller@10490000 {
++ compatible = "starfive,jh7100-audrst";
++ reg = <0x10490000 0x10000>;
++ #reset-cells = <1>;
++ };
++
++...
--- /dev/null
+From 91559ced47de9e6fb1e6dd65a5544fcc86dea806 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 19:30:49 +0100
+Subject: [PATCH 1014/1024] reset: starfive: Add JH7100 audio reset driver
+
+The audio resets are almost identical to the system resets, there are
+just fewer of them. So factor out and export a generic probe function,
+so most of the reset controller implementation can be shared.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ MAINTAINERS | 2 +-
+ drivers/reset/starfive/Kconfig | 7 ++
+ drivers/reset/starfive/Makefile | 2 +
+ .../starfive/reset-starfive-jh7100-audio.c | 66 +++++++++++++++++++
+ .../reset/starfive/reset-starfive-jh7100.h | 16 +++++
+ 5 files changed, 92 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/reset/starfive/reset-starfive-jh7100-audio.c
+ create mode 100644 drivers/reset/starfive/reset-starfive-jh7100.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -20554,7 +20554,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS
+ M: Emil Renner Berthing <kernel@esmil.dk>
+ M: Hal Feng <hal.feng@starfivetech.com>
+ S: Maintained
+-F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml
++F: Documentation/devicetree/bindings/reset/starfive,jh7100-*.yaml
+ F: drivers/reset/starfive/reset-starfive-jh71*
+ F: include/dt-bindings/reset/starfive?jh71*.h
+
+--- a/drivers/reset/starfive/Kconfig
++++ b/drivers/reset/starfive/Kconfig
+@@ -11,6 +11,13 @@ config RESET_STARFIVE_JH7100
+ help
+ This enables the reset controller driver for the StarFive JH7100 SoC.
+
++config RESET_STARFIVE_JH7100_AUDIO
++ tristate "StarFive JH7100 Audio Reset Driver"
++ depends on RESET_STARFIVE_JH7100
++ default m if SOC_STARFIVE
++ help
++ This enables the audio reset driver for the StarFive JH7100 SoC.
++
+ config RESET_STARFIVE_JH7110
+ bool "StarFive JH7110 Reset Driver"
+ depends on CLK_STARFIVE_JH7110_SYS
+--- a/drivers/reset/starfive/Makefile
++++ b/drivers/reset/starfive/Makefile
+@@ -2,4 +2,6 @@
+ obj-$(CONFIG_RESET_STARFIVE_JH71X0) += reset-starfive-jh71x0.o
+
+ obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o
++obj-$(CONFIG_RESET_STARFIVE_JH7100_AUDIO) += reset-starfive-jh7100-audio.o
++
+ obj-$(CONFIG_RESET_STARFIVE_JH7110) += reset-starfive-jh7110.o
+--- /dev/null
++++ b/drivers/reset/starfive/reset-starfive-jh7100-audio.c
+@@ -0,0 +1,66 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Audio reset driver for the StarFive JH7100 SoC
++ *
++ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
++ */
++
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "reset-starfive-jh71x0.h"
++
++#include <dt-bindings/reset/starfive-jh7100-audio.h>
++
++/* register offsets */
++#define JH7100_AUDRST_ASSERT0 0x00
++#define JH7100_AUDRST_STATUS0 0x04
++
++/*
++ * Writing a 1 to the n'th bit of the ASSERT register asserts
++ * line n, and writing a 0 deasserts the same line.
++ * Most reset lines have their status inverted so a 0 bit in the STATUS
++ * register means the line is asserted and a 1 means it's deasserted. A few
++ * lines don't though, so store the expected value of the status registers when
++ * all lines are asserted.
++ */
++static const u32 jh7100_audrst_asserted[1] = {
++ BIT(JH7100_AUDRST_USB_AXI) |
++ BIT(JH7100_AUDRST_USB_PWRUP_RST_N) |
++ BIT(JH7100_AUDRST_USB_PONRST)
++};
++
++static int jh7100_audrst_probe(struct platform_device *pdev)
++{
++ void __iomem *base = devm_platform_ioremap_resource(pdev, 0);
++
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ return reset_starfive_jh71x0_register(&pdev->dev, pdev->dev.of_node,
++ base + JH7100_AUDRST_ASSERT0,
++ base + JH7100_AUDRST_STATUS0,
++ jh7100_audrst_asserted,
++ JH7100_AUDRSTN_END,
++ THIS_MODULE);
++}
++
++static const struct of_device_id jh7100_audrst_dt_ids[] = {
++ { .compatible = "starfive,jh7100-audrst" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7100_audrst_dt_ids);
++
++static struct platform_driver jh7100_audrst_driver = {
++ .probe = jh7100_audrst_probe,
++ .driver = {
++ .name = "jh7100-reset-audio",
++ .of_match_table = jh7100_audrst_dt_ids,
++ },
++};
++module_platform_driver(jh7100_audrst_driver);
++
++MODULE_AUTHOR("Emil Renner Berthing");
++MODULE_DESCRIPTION("StarFive JH7100 audio reset driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/reset/starfive/reset-starfive-jh7100.h
+@@ -0,0 +1,16 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
++ */
++
++#ifndef _RESET_STARFIVE_JH7100_H_
++#define _RESET_STARFIVE_JH7100_H_
++
++#include <linux/platform_device.h>
++
++int reset_starfive_jh7100_generic_probe(struct platform_device *pdev,
++ const u32 *asserted,
++ unsigned int status_offset,
++ unsigned int nr_resets);
++
++#endif
--- /dev/null
+From 418603fdce51212d4547aacfe2b4801fc5e61978 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 21:33:08 +0100
+Subject: [PATCH 1015/1024] RISC-V: Add StarFive JH7100 audio reset node
+
+Add device tree node for the audio resets on the StarFive JH7100 RISC-V
+SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -168,6 +168,12 @@
+ #clock-cells = <1>;
+ };
+
++ audrst: reset-controller@10490000 {
++ compatible = "starfive,jh7100-audrst";
++ reg = <0x0 0x10490000 0x0 0x10000>;
++ #reset-cells = <1>;
++ };
++
+ clkgen: clock-controller@11800000 {
+ compatible = "starfive,jh7100-clkgen";
+ reg = <0x0 0x11800000 0x0 0x10000>;
--- /dev/null
+From 8e090d271683d5869cdab0729f54a8af8c79c476 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Tue, 31 Oct 2023 15:14:44 +0100
+Subject: [PATCH 1016/1024] soc: sifive: ccache: Add StarFive JH7100 support
+
+This adds support for the StarFive JH7100 SoC which also features this
+SiFive cache controller.
+
+The JH7100 has non-coherent DMAs but predate the standard RISC-V Zicbom
+exension, so instead we need to use this cache controller for
+non-standard cache management operations.
+
+Unfortunately the interrupt for uncorrected data is broken on the JH7100
+and fires continuously, so add a quirk to not register a handler for it.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/soc/sifive/sifive_ccache.c | 62 +++++++++++++++++++++++++++++-
+ 1 file changed, 60 insertions(+), 2 deletions(-)
+
+--- a/drivers/soc/sifive/sifive_ccache.c
++++ b/drivers/soc/sifive/sifive_ccache.c
+@@ -8,13 +8,16 @@
+
+ #define pr_fmt(fmt) "CCACHE: " fmt
+
++#include <linux/align.h>
+ #include <linux/debugfs.h>
+ #include <linux/interrupt.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_address.h>
+ #include <linux/device.h>
+ #include <linux/bitfield.h>
++#include <asm/cacheflush.h>
+ #include <asm/cacheinfo.h>
++#include <asm/dma-noncoherent.h>
+ #include <soc/sifive/sifive_ccache.h>
+
+ #define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100
+@@ -39,10 +42,14 @@
+ #define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16)
+ #define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24)
+
++#define SIFIVE_CCACHE_FLUSH64 0x200
++#define SIFIVE_CCACHE_FLUSH32 0x240
++
+ #define SIFIVE_CCACHE_WAYENABLE 0x08
+ #define SIFIVE_CCACHE_ECCINJECTERR 0x40
+
+ #define SIFIVE_CCACHE_MAX_ECCINTR 4
++#define SIFIVE_CCACHE_LINE_SIZE 64
+
+ static void __iomem *ccache_base;
+ static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR];
+@@ -56,6 +63,11 @@ enum {
+ DIR_UNCORR,
+ };
+
++enum {
++ QUIRK_NONSTANDARD_CACHE_OPS = BIT(0),
++ QUIRK_BROKEN_DATA_UNCORR = BIT(1),
++};
++
+ #ifdef CONFIG_DEBUG_FS
+ static struct dentry *sifive_test;
+
+@@ -106,6 +118,8 @@ static void ccache_config_read(void)
+ static const struct of_device_id sifive_ccache_ids[] = {
+ { .compatible = "sifive,fu540-c000-ccache" },
+ { .compatible = "sifive,fu740-c000-ccache" },
++ { .compatible = "starfive,jh7100-ccache",
++ .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS | QUIRK_BROKEN_DATA_UNCORR) },
+ { .compatible = "sifive,ccache0" },
+ { /* end of table */ }
+ };
+@@ -124,6 +138,34 @@ int unregister_sifive_ccache_error_notif
+ }
+ EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier);
+
++#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
++static void ccache_flush_range(phys_addr_t start, size_t len)
++{
++ phys_addr_t end = start + len;
++ phys_addr_t line;
++
++ if (!len)
++ return;
++
++ mb();
++ for (line = ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); line < end;
++ line += SIFIVE_CCACHE_LINE_SIZE) {
++#ifdef CONFIG_32BIT
++ writel(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32);
++#else
++ writeq(line, ccache_base + SIFIVE_CCACHE_FLUSH64);
++#endif
++ mb();
++ }
++}
++
++static const struct riscv_nonstd_cache_ops ccache_mgmt_ops __initconst = {
++ .wback = &ccache_flush_range,
++ .inv = &ccache_flush_range,
++ .wback_inv = &ccache_flush_range,
++};
++#endif /* CONFIG_RISCV_NONSTANDARD_CACHE_OPS */
++
+ static int ccache_largest_wayenabled(void)
+ {
+ return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF;
+@@ -210,11 +252,15 @@ static int __init sifive_ccache_init(voi
+ struct device_node *np;
+ struct resource res;
+ int i, rc, intr_num;
++ const struct of_device_id *match;
++ unsigned long quirks;
+
+- np = of_find_matching_node(NULL, sifive_ccache_ids);
++ np = of_find_matching_node_and_match(NULL, sifive_ccache_ids, &match);
+ if (!np)
+ return -ENODEV;
+
++ quirks = (uintptr_t)match->data;
++
+ if (of_address_to_resource(np, 0, &res)) {
+ rc = -ENODEV;
+ goto err_node_put;
+@@ -240,6 +286,10 @@ static int __init sifive_ccache_init(voi
+
+ for (i = 0; i < intr_num; i++) {
+ g_irq[i] = irq_of_parse_and_map(np, i);
++
++ if (i == DATA_UNCORR && (quirks & QUIRK_BROKEN_DATA_UNCORR))
++ continue;
++
+ rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc",
+ NULL);
+ if (rc) {
+@@ -249,6 +299,14 @@ static int __init sifive_ccache_init(voi
+ }
+ of_node_put(np);
+
++#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
++ if (quirks & QUIRK_NONSTANDARD_CACHE_OPS) {
++ riscv_cbom_block_size = SIFIVE_CCACHE_LINE_SIZE;
++ riscv_noncoherent_supported();
++ riscv_noncoherent_register_cache_ops(&ccache_mgmt_ops);
++ }
++#endif
++
+ ccache_config_read();
+
+ ccache_cache_ops.get_priv_group = ccache_get_priv_group;
+@@ -269,4 +327,4 @@ err_node_put:
+ return rc;
+ }
+
+-device_initcall(sifive_ccache_init);
++arch_initcall(sifive_ccache_init);
--- /dev/null
+From 30fb5963f4cf3b7d114a8212358147615480685c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:25 +0100
+Subject: [PATCH 1017/1024] riscv: errata: Add StarFive JH7100 errata
+
+This not really an errata, but since the JH7100 was made before
+the standard Zicbom extension it needs the DMA_GLOBAL_POOL and
+RISCV_NONSTANDARD_CACHE_OPS enabled to work correctly.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
+Acked-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/Kconfig.errata | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/arch/riscv/Kconfig.errata
++++ b/arch/riscv/Kconfig.errata
+@@ -53,6 +53,23 @@ config ERRATA_SIFIVE_CIP_1200
+
+ If you don't know what to do here, say "Y".
+
++config ERRATA_STARFIVE_JH7100
++ bool "StarFive JH7100 support"
++ depends on ARCH_STARFIVE && NONPORTABLE
++ select DMA_GLOBAL_POOL
++ select RISCV_DMA_NONCOHERENT
++ select RISCV_NONSTANDARD_CACHE_OPS
++ select SIFIVE_CCACHE
++ default n
++ help
++ The StarFive JH7100 was a test chip for the JH7110 and has
++ caches that are non-coherent with respect to peripheral DMAs.
++ It was designed before the Zicbom extension so needs non-standard
++ cache operations through the SiFive cache controller.
++
++ Say "Y" if you want to support the BeagleV Starlight and/or
++ StarFive VisionFive V1 boards.
++
+ config ERRATA_THEAD
+ bool "T-HEAD errata"
+ depends on RISCV_ALTERNATIVE
--- /dev/null
+From 29e4bc0fafd9add93acc967f3992948b3afe7176 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 30 Nov 2023 16:19:27 +0100
+Subject: [PATCH 1018/1024] riscv: dts: starfive: Mark the JH7100 as having
+ non-coherent DMAs
+
+The StarFive JH7100 SoC has non-coherent device DMAs, so mark the
+soc bus as such.
+
+Link: https://github.com/starfive-tech/JH7100_Docs/blob/main/JH7100%20Cache%20Coherence%20V1.0.pdf
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -138,6 +138,7 @@
+ interrupt-parent = <&plic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
++ dma-noncoherent;
+ ranges;
+
+ clint: clint@2000000 {
--- /dev/null
+From e1918356dcc285eb7c50f271795e6fcc18d6c092 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:28 +0100
+Subject: [PATCH 1019/1024] riscv: dts: starfive: Add JH7100 cache controller
+
+The StarFive JH7100 SoC also features the SiFive L2 cache controller,
+so add the device tree nodes for it.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -32,6 +32,7 @@
+ i-tlb-sets = <1>;
+ i-tlb-size = <32>;
+ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
+
+@@ -57,6 +58,7 @@
+ i-tlb-sets = <1>;
+ i-tlb-size = <32>;
+ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
+
+@@ -148,6 +150,17 @@
+ &cpu1_intc 3 &cpu1_intc 7>;
+ };
+
++ ccache: cache-controller@2010000 {
++ compatible = "starfive,jh7100-ccache", "sifive,ccache0", "cache";
++ reg = <0x0 0x2010000 0x0 0x1000>;
++ interrupts = <128>, <130>, <131>, <129>;
++ cache-block-size = <64>;
++ cache-level = <2>;
++ cache-sets = <2048>;
++ cache-size = <2097152>;
++ cache-unified;
++ };
++
+ plic: interrupt-controller@c000000 {
+ compatible = "starfive,jh7100-plic", "sifive,plic-1.0.0";
+ reg = <0x0 0xc000000 0x0 0x4000000>;
--- /dev/null
+From 3cbd661b811bda9a33253f65b5cf0c25b8c5447f Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:29 +0100
+Subject: [PATCH 1020/1024] riscv: dts: starfive: Add pool for coherent DMA
+ memory on JH7100 boards
+
+The StarFive JH7100 SoC has non-coherent device DMAs, but most drivers
+expect to be able to allocate coherent memory for DMA descriptors and
+such. However on the JH7100 DDR memory appears twice in the physical
+memory map, once cached and once uncached:
+
+ 0x00_8000_0000 - 0x08_7fff_ffff : Off chip DDR memory, cached
+ 0x10_0000_0000 - 0x17_ffff_ffff : Off chip DDR memory, uncached
+
+To use this uncached region we create a global DMA memory pool there and
+reserve the corresponding area in the cached region.
+
+However the uncached region is fully above the 32bit address limit, so add
+a dma-ranges map so the DMA address used for peripherals is still in the
+regular cached region below the limit.
+
+Link: https://github.com/starfive-tech/JH7100_Docs/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../boot/dts/starfive/jh7100-common.dtsi | 24 +++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -39,6 +39,30 @@
+ label = "ack";
+ };
+ };
++
++ reserved-memory {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges;
++
++ dma-reserved@fa000000 {
++ reg = <0x0 0xfa000000 0x0 0x1000000>;
++ no-map;
++ };
++
++ linux,dma@107a000000 {
++ compatible = "shared-dma-pool";
++ reg = <0x10 0x7a000000 0x0 0x1000000>;
++ no-map;
++ linux,dma-default;
++ };
++ };
++
++ soc {
++ dma-ranges = <0x00 0x80000000 0x00 0x80000000 0x00 0x7a000000>,
++ <0x00 0xfa000000 0x10 0x7a000000 0x00 0x01000000>,
++ <0x00 0xfb000000 0x00 0xfb000000 0x07 0x85000000>;
++ };
+ };
+
+ &gpio {
--- /dev/null
+From 7be159c760aa8a1ece1354892af215b2f8c21152 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:30 +0100
+Subject: [PATCH 1021/1024] riscv: dts: starfive: Add JH7100 MMC nodes
+
+Add device tree nodes for the Synopsis MMC controllers on the
+StarFive JH7100 SoC.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 26 ++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -188,6 +188,32 @@
+ #reset-cells = <1>;
+ };
+
++ sdio0: mmc@10000000 {
++ compatible = "snps,dw-mshc";
++ reg = <0x0 0x10000000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SDIO0_AHB>,
++ <&clkgen JH7100_CLK_SDIO0_CCLKINT_INV>;
++ clock-names = "biu", "ciu";
++ interrupts = <4>;
++ data-addr = <0>;
++ fifo-depth = <32>;
++ fifo-watermark-aligned;
++ status = "disabled";
++ };
++
++ sdio1: mmc@10010000 {
++ compatible = "snps,dw-mshc";
++ reg = <0x0 0x10010000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SDIO1_AHB>,
++ <&clkgen JH7100_CLK_SDIO1_CCLKINT_INV>;
++ clock-names = "biu", "ciu";
++ interrupts = <5>;
++ data-addr = <0>;
++ fifo-depth = <32>;
++ fifo-watermark-aligned;
++ status = "disabled";
++ };
++
+ clkgen: clock-controller@11800000 {
+ compatible = "starfive,jh7100-clkgen";
+ reg = <0x0 0x11800000 0x0 0x10000>;
--- /dev/null
+From 015edaccef82200d913d5f1e99fd95641f526bc6 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:31 +0100
+Subject: [PATCH 1022/1024] riscv: dts: starfive: Enable SD-card on JH7100
+ boards
+
+Add pinctrl and MMC device tree nodes for the SD-card on the
+BeagleV Starlight and StarFive VisionFive V1 boards.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../boot/dts/starfive/jh7100-common.dtsi | 47 +++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -12,6 +12,7 @@
+
+ / {
+ aliases {
++ mmc0 = &sdio0;
+ serial0 = &uart3;
+ };
+
+@@ -108,6 +109,43 @@
+ };
+ };
+
++ sdio0_pins: sdio0-0 {
++ clk-pins {
++ pinmux = <GPIOMUX(54, GPO_SDIO0_PAD_CCLK_OUT,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ sdio-pins {
++ pinmux = <GPIOMUX(55, GPO_LOW, GPO_DISABLE,
++ GPI_SDIO0_PAD_CARD_DETECT_N)>,
++ <GPIOMUX(53,
++ GPO_SDIO0_PAD_CCMD_OUT,
++ GPO_SDIO0_PAD_CCMD_OEN,
++ GPI_SDIO0_PAD_CCMD_IN)>,
++ <GPIOMUX(49,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT0,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT0,
++ GPI_SDIO0_PAD_CDATA_IN_BIT0)>,
++ <GPIOMUX(50,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT1,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT1,
++ GPI_SDIO0_PAD_CDATA_IN_BIT1)>,
++ <GPIOMUX(51,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT2,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT2,
++ GPI_SDIO0_PAD_CDATA_IN_BIT2)>,
++ <GPIOMUX(52,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT3,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT3,
++ GPI_SDIO0_PAD_CDATA_IN_BIT3)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
+ uart3_pins: uart3-0 {
+ rx-pins {
+ pinmux = <GPIOMUX(13, GPO_LOW, GPO_DISABLE,
+@@ -178,6 +216,15 @@
+ clock-frequency = <27000000>;
+ };
+
++&sdio0 {
++ broken-cd;
++ bus-width = <4>;
++ cap-sd-highspeed;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdio0_pins>;
++ status = "okay";
++};
++
+ &uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
--- /dev/null
+From 1e70a0772165dd552f82434c9072dabfaaaf4c2a Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Fri, 15 Dec 2023 20:09:09 +0100
+Subject: [PATCH 1023/1024] riscv: errata: Make ERRATA_STARFIVE_JH7100 depend
+ on !DMA_DIRECT_REMAP
+
+Similar to the Renesas RZ/Five[1] the JH7100 SoC needs the non-portable
+CONFIG_DMA_GLOBAL_POOL enabled which is incompatible with DMA_DIRECT_REMAP
+selected by RISCV_ISA_ZICBOM.
+
+[1]: commit 31b2daea0764 ("soc: renesas: Make RZ/Five depend on !DMA_DIRECT_REMAP")
+
+Link: https://lore.kernel.org/all/24942b4d-d16a-463f-b39a-f9dfcb89d742@infradead.org/
+Fixes: 64fc984a8a54 ("riscv: errata: Add StarFive JH7100 errata")
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/Kconfig.errata | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/riscv/Kconfig.errata
++++ b/arch/riscv/Kconfig.errata
+@@ -55,7 +55,9 @@ config ERRATA_SIFIVE_CIP_1200
+
+ config ERRATA_STARFIVE_JH7100
+ bool "StarFive JH7100 support"
+- depends on ARCH_STARFIVE && NONPORTABLE
++ depends on ARCH_STARFIVE
++ depends on !DMA_DIRECT_REMAP
++ depends on NONPORTABLE
+ select DMA_GLOBAL_POOL
+ select RISCV_DMA_NONCOHERENT
+ select RISCV_NONSTANDARD_CACHE_OPS
--- /dev/null
+From 782f99cc437d975c9ef5a1f351bb8fb83d50039b Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sun, 1 Sep 2024 12:43:01 +0000
+Subject: [PATCH 1024/1024] riscv: dts: Add full JH7100, Starlight and
+ VisionFive support
+
+Based on the device tree in https://github.com/starfive-tech/u-boot/
+with contributions from:
+yanhong.wang <yanhong.wang@starfivetech.com>
+Huan.Feng <huan.feng@starfivetech.com>
+ke.zhu <ke.zhu@starfivetech.com>
+yiming.li <yiming.li@starfivetech.com>
+jack.zhu <jack.zhu@starfivetech.com>
+Samin Guo <samin.guo@starfivetech.com>
+Chenjieqin <Jessica.Chen@starfivetech.com>
+bo.li <bo.li@starfivetech.com>
+
+Rearranged, cleanups, fixes, pins and resets added by Emil.
+Cleanups, fixes, clocks added by Geert.
+Cleanups and GPIO fixes from Drew.
+Thermal zone added by Stephen.
+PWM pins added by Jianlong.
+cpu-map added by Jonas.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
+Signed-off-by: Stephen L Arnold <nerdboy@gentoo.org>
+Signed-off-by: Drew Fustini <drew@beagleboard.org>
+Signed-off-by: Jianlong Huang <jianlong.huang@starfivetech.com>
+Signed-off-by: Jonas Hahnfeld <hahnjo@hahnjo.de>
+---
+ .../dts/starfive/jh7100-beaglev-starlight.dts | 16 +
+ .../boot/dts/starfive/jh7100-common.dtsi | 432 +++++++++++++++
+ .../jh7100-starfive-visionfive-v1.dts | 19 +
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 503 ++++++++++++++++++
+ 4 files changed, 970 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts
++++ b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts
+@@ -6,8 +6,24 @@
+
+ /dts-v1/;
+ #include "jh7100-common.dtsi"
++#include <dt-bindings/gpio/gpio.h>
+
+ / {
+ model = "BeagleV Starlight Beta";
+ compatible = "beagle,beaglev-starlight-jh7100-r0", "starfive,jh7100";
+ };
++
++&gmac {
++ snps,reset-gpios = <&gpio 63 GPIO_ACTIVE_LOW>;
++};
++
++&gpio {
++ /* don't reset gpio mux for serial console on uart3 */
++ starfive,keep-gpiomux = <13 14>;
++};
++
++&mdio {
++ phy: ethernet-phy@7 {
++ reg = <7>;
++ };
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -64,9 +64,174 @@
+ <0x00 0xfa000000 0x10 0x7a000000 0x00 0x01000000>,
+ <0x00 0xfb000000 0x00 0xfb000000 0x07 0x85000000>;
+ };
++
++ reserved-memory {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges;
++
++ linux,cma {
++ compatible = "shared-dma-pool";
++ reusable;
++ size = <0x0 0x28000000>;
++ alignment = <0x0 0x1000>;
++ alloc-ranges = <0x0 0xa0000000 0x0 0x28000000>;
++ linux,cma-default;
++ };
++
++ jpu_reserved: framebuffer@c9000000 {
++ reg = <0x0 0xc9000000 0x0 0x4000000>;
++ };
++
++ nvdla_reserved: framebuffer@d0000000 {
++ no-map;
++ reg = <0x0 0xd0000000 0x0 0x28000000>;
++ };
++
++ vin_reserved: framebuffer@f9000000 {
++ compatible = "shared-dma-pool";
++ no-map;
++ reg = <0x0 0xf9000000 0x0 0x1000000>;
++ };
++
++ sffb_reserved: framebuffer@fb000000 {
++ compatible = "shared-dma-pool";
++ no-map;
++ reg = <0x0 0xfb000000 0x0 0x2000000>;
++ };
++ };
++
++ wifi_pwrseq: wifi-pwrseq {
++ compatible = "mmc-pwrseq-simple";
++ reset-gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
++ };
++};
++
++&display {
++ memory-region = <&sffb_reserved>;
++ status = "okay";
++};
++
++&crtc {
++ ddr-format = <4>; //<WIN_FMT_RGB565>;
++ status = "okay";
++
++ port: port@0 {
++ reg = <0>;
++
++ crtc_0_out: endpoint {
++ remote-endpoint = <&hdmi_input0>;
++ };
++ };
++};
++
++&encoder {
++ encoder-type = <2>; // 2-TMDS, 3-LVDS, 6-DSI, 8-DPI
++ status = "okay";
++
++ ports {
++ port@0 {
++ hdmi_out: endpoint {
++ remote-endpoint = <&tda998x_0_input>;
++ };
++ };
++
++ port@1 {
++ hdmi_input0: endpoint {
++ remote-endpoint = <&crtc_0_out>;
++ };
++ };
++
++ };
++};
++
++&gmac {
++ starfive,gtxclk-dlychain = <4>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&gmac_pins>;
++ phy-mode = "rgmii-txid";
++ phy-handle = <&phy>;
++ status = "okay";
++
++ mdio: mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "snps,dwmac-mdio";
++ };
+ };
+
+ &gpio {
++ gmac_pins: gmac-0 {
++ gtxclk-pins {
++ pins = <PAD_FUNC_SHARE(115)>;
++ bias-pull-up;
++ drive-strength = <35>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++ miitxclk-pins {
++ pins = <PAD_FUNC_SHARE(116)>;
++ bias-pull-up;
++ drive-strength = <14>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ tx-pins {
++ pins = <PAD_FUNC_SHARE(117)>,
++ <PAD_FUNC_SHARE(119)>,
++ <PAD_FUNC_SHARE(120)>,
++ <PAD_FUNC_SHARE(121)>,
++ <PAD_FUNC_SHARE(122)>,
++ <PAD_FUNC_SHARE(123)>,
++ <PAD_FUNC_SHARE(124)>,
++ <PAD_FUNC_SHARE(125)>,
++ <PAD_FUNC_SHARE(126)>;
++ bias-pull-up;
++ drive-strength = <35>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ rxclk-pins {
++ pins = <PAD_FUNC_SHARE(127)>;
++ bias-pull-up;
++ drive-strength = <14>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <6>;
++ };
++ rxer-pins {
++ pins = <PAD_FUNC_SHARE(129)>;
++ bias-pull-up;
++ drive-strength = <14>;
++ input-enable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ rx-pins {
++ pins = <PAD_FUNC_SHARE(128)>,
++ <PAD_FUNC_SHARE(130)>,
++ <PAD_FUNC_SHARE(131)>,
++ <PAD_FUNC_SHARE(132)>,
++ <PAD_FUNC_SHARE(133)>,
++ <PAD_FUNC_SHARE(134)>,
++ <PAD_FUNC_SHARE(135)>,
++ <PAD_FUNC_SHARE(136)>,
++ <PAD_FUNC_SHARE(137)>,
++ <PAD_FUNC_SHARE(138)>,
++ <PAD_FUNC_SHARE(139)>,
++ <PAD_FUNC_SHARE(140)>,
++ <PAD_FUNC_SHARE(141)>;
++ bias-pull-up;
++ drive-strength = <14>;
++ input-enable;
++ input-schmitt-enable;
++ slew-rate = <0>;
++ };
++ };
++
+ i2c0_pins: i2c0-0 {
+ i2c-pins {
+ pinmux = <GPIOMUX(62, GPO_LOW,
+@@ -146,6 +311,166 @@
+ };
+ };
+
++ pwmdac_pins: pwmdac-0 {
++ pwmdac-pins {
++ pinmux = <GPIOMUX(23, GPO_PWMDAC_LEFT_OUT,
++ GPO_ENABLE, GPI_NONE)>,
++ <GPIOMUX(24, GPO_PWMDAC_RIGHT_OUT,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ drive-strength = <35>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
++
++ pwm_pins: pwm-0 {
++ pwm-pins {
++ pinmux = <GPIOMUX(7,
++ GPO_PWM_PAD_OUT_BIT0,
++ GPO_PWM_PAD_OE_N_BIT0,
++ GPI_NONE)>,
++ <GPIOMUX(5,
++ GPO_PWM_PAD_OUT_BIT1,
++ GPO_PWM_PAD_OE_N_BIT1,
++ GPI_NONE)>;
++ bias-disable;
++ drive-strength = <35>;
++ input-disable;
++ input-schmitt-disable;
++ slew-rate = <0>;
++ };
++ };
++
++ sdio0_pins: sdio0-0 {
++ clk-pins {
++ pinmux = <GPIOMUX(54, GPO_SDIO0_PAD_CCLK_OUT,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ sdio-pins {
++ pinmux = <GPIOMUX(55, GPO_LOW, GPO_DISABLE,
++ GPI_SDIO0_PAD_CARD_DETECT_N)>,
++ <GPIOMUX(53,
++ GPO_SDIO0_PAD_CCMD_OUT,
++ GPO_SDIO0_PAD_CCMD_OEN,
++ GPI_SDIO0_PAD_CCMD_IN)>,
++ <GPIOMUX(49,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT0,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT0,
++ GPI_SDIO0_PAD_CDATA_IN_BIT0)>,
++ <GPIOMUX(50,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT1,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT1,
++ GPI_SDIO0_PAD_CDATA_IN_BIT1)>,
++ <GPIOMUX(51,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT2,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT2,
++ GPI_SDIO0_PAD_CDATA_IN_BIT2)>,
++ <GPIOMUX(52,
++ GPO_SDIO0_PAD_CDATA_OUT_BIT3,
++ GPO_SDIO0_PAD_CDATA_OEN_BIT3,
++ GPI_SDIO0_PAD_CDATA_IN_BIT3)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ sdio1_pins: sdio1-0 {
++ clk-pins {
++ pinmux = <GPIOMUX(33, GPO_SDIO1_PAD_CCLK_OUT,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ sdio-pins {
++ pinmux = <GPIOMUX(29,
++ GPO_SDIO1_PAD_CCMD_OUT,
++ GPO_SDIO1_PAD_CCMD_OEN,
++ GPI_SDIO1_PAD_CCMD_IN)>,
++ <GPIOMUX(36,
++ GPO_SDIO1_PAD_CDATA_OUT_BIT0,
++ GPO_SDIO1_PAD_CDATA_OEN_BIT0,
++ GPI_SDIO1_PAD_CDATA_IN_BIT0)>,
++ <GPIOMUX(30,
++ GPO_SDIO1_PAD_CDATA_OUT_BIT1,
++ GPO_SDIO1_PAD_CDATA_OEN_BIT1,
++ GPI_SDIO1_PAD_CDATA_IN_BIT1)>,
++ <GPIOMUX(34,
++ GPO_SDIO1_PAD_CDATA_OUT_BIT2,
++ GPO_SDIO1_PAD_CDATA_OEN_BIT2,
++ GPI_SDIO1_PAD_CDATA_IN_BIT2)>,
++ <GPIOMUX(31,
++ GPO_SDIO1_PAD_CDATA_OUT_BIT3,
++ GPO_SDIO1_PAD_CDATA_OEN_BIT3,
++ GPI_SDIO1_PAD_CDATA_IN_BIT3)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ };
++
++ spi2_pins: spi2-0 {
++ mosi-pins {
++ pinmux = <GPIOMUX(18, GPO_SPI2_PAD_TXD,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ miso-pins {
++ pinmux = <GPIOMUX(16, GPO_LOW, GPO_DISABLE,
++ GPI_SPI2_PAD_RXD)>;
++ bias-pull-up;
++ input-enable;
++ input-schmitt-enable;
++ };
++ sck-pins {
++ pinmux = <GPIOMUX(12, GPO_SPI2_PAD_SCK_OUT,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ ss-pins {
++ pinmux = <GPIOMUX(15, GPO_SPI2_PAD_SS_0_N,
++ GPO_ENABLE, GPI_NONE)>,
++ <GPIOMUX(11, GPO_SPI2_PAD_SS_1_N,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
++ uart0_pins: uart0-0 {
++ rx-pins {
++ pinmux = <GPIOMUX(40, GPO_LOW, GPO_DISABLE,
++ GPI_UART0_PAD_SIN)>,
++ <GPIOMUX(39, GPO_LOW, GPO_DISABLE,
++ GPI_UART0_PAD_CTSN)>;
++ bias-pull-up;
++ drive-strength = <14>;
++ input-enable;
++ input-schmitt-enable;
++ };
++ tx-pins {
++ pinmux = <GPIOMUX(41, GPO_UART0_PAD_SOUT,
++ GPO_ENABLE, GPI_NONE)>,
++ <GPIOMUX(42, GPO_UART0_PAD_RTSN,
++ GPO_ENABLE, GPI_NONE)>;
++ bias-disable;
++ drive-strength = <35>;
++ input-disable;
++ input-schmitt-disable;
++ };
++ };
++
+ uart3_pins: uart3-0 {
+ rx-pins {
+ pinmux = <GPIOMUX(13, GPO_LOW, GPO_DISABLE,
+@@ -186,6 +511,17 @@
+ regulators {
+ };
+ };
++
++ tda998x@70 {
++ compatible = "nxp,tda998x";
++ reg = <0x70>;
++
++ port {
++ tda998x_0_input: endpoint {
++ remote-endpoint = <&hdmi_out>;
++ };
++ };
++ };
+ };
+
+ &i2c1 {
+@@ -225,8 +561,104 @@
+ status = "okay";
+ };
+
++&ptc {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pins>;
++ status = "okay";
++};
++
++&pwmdac {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwmdac_pins>;
++ status = "okay";
++};
++
++&qspi {
++ nor_flash: nor-flash@0 {
++ compatible = "spi-flash";
++ reg = <0>;
++ spi-max-frequency = <31250000>;
++ page-size = <256>;
++ block-size = <16>;
++ cdns,read-delay = <4>;
++ cdns,tshsl-ns = <1>;
++ cdns,tsd2d-ns = <1>;
++ cdns,tchsh-ns = <1>;
++ cdns,tslch-ns = <1>;
++ spi-tx-bus-width = <1>;
++ spi-rx-bus-width = <1>;
++ };
++
++ nand_flash: nand-flash@1 {
++ compatible = "spi-flash-nand";
++ reg = <1>;
++ spi-max-frequency = <31250000>;
++ page-size = <2048>;
++ block-size = <17>;
++ cdns,read-delay = <4>;
++ cdns,tshsl-ns = <1>;
++ cdns,tsd2d-ns = <1>;
++ cdns,tchsh-ns = <1>;
++ cdns,tslch-ns = <1>;
++ spi-tx-bus-width = <1>;
++ spi-rx-bus-width = <1>;
++ };
++};
++
++&sdio0 {
++ broken-cd;
++ bus-width = <4>;
++ cap-sd-highspeed;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdio0_pins>;
++ max-frequency = <10000000>;
++ status = "okay";
++};
++
++&sdio1 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ bus-width = <4>;
++ cap-sd-highspeed;
++ cap-sdio-irq;
++ cap-power-off-card;
++ mmc-pwrseq = <&wifi_pwrseq>;
++ non-removable;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdio1_pins>;
++ status = "okay";
++
++ wifi@1 {
++ compatible = "brcm,bcm4329-fmac";
++ reg = <1>;
++ };
++};
++
++&spi2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi2_pins>;
++ status = "okay";
++
++ spi_dev0: spi@0 {
++ compatible = "rohm,dh2228fv";
++ spi-max-frequency = <10000000>;
++ reg = <0>;
++ };
++};
++
++&uart0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart0_pins>;
++ status = "okay";
++};
++
+ &uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+ status = "okay";
+ };
++
++&usb3 {
++ dr_mode = "host";
++ status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100-starfive-visionfive-v1.dts
++++ b/arch/riscv/boot/dts/starfive/jh7100-starfive-visionfive-v1.dts
+@@ -18,3 +18,22 @@
+ priority = <224>;
+ };
+ };
++
++&gpio {
++ /* don't reset gpio mux for serial console and reset gpio */
++ starfive,keep-gpiomux = <13 14 63>;
++};
++
++&i2c0 {
++ eeprom@50 {
++ compatible = "atmel,24c04";
++ reg = <0x50>;
++ pagesize = <16>;
++ };
++};
++
++&mdio {
++ phy: ethernet-phy@0 {
++ reg = <0>;
++ };
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -6,7 +6,9 @@
+
+ /dts-v1/;
+ #include <dt-bindings/clock/starfive-jh7100.h>
++#include <dt-bindings/clock/starfive-jh7100-audio.h>
+ #include <dt-bindings/reset/starfive-jh7100.h>
++#include <dt-bindings/reset/starfive-jh7100-audio.h>
+
+ / {
+ compatible = "starfive,jh7100";
+@@ -135,6 +137,13 @@
+ clock-frequency = <0>;
+ };
+
++ /* gmac device configuration */
++ stmmac_axi_setup: stmmac-axi-config {
++ snps,wr_osr_lmt = <0xf>;
++ snps,rd_osr_lmt = <0xf>;
++ snps,blen = <256 128 64 32 0 0 0>;
++ };
++
+ soc {
+ compatible = "simple-bus";
+ interrupt-parent = <&plic>;
+@@ -143,6 +152,24 @@
+ dma-noncoherent;
+ ranges;
+
++ dtim: dtim@1000000 {
++ compatible = "starfive,dtim0";
++ reg = <0x0 0x1000000 0x0 0x2000>;
++ reg-names = "mem";
++ };
++
++ itim0: itim@1808000 {
++ compatible = "starfive,itim0";
++ reg = <0x0 0x1808000 0x0 0x8000>;
++ reg-names = "mem";
++ };
++
++ itim1: itim@1820000 {
++ compatible = "starfive,itim0";
++ reg = <0x0 0x1820000 0x0 0x8000>;
++ reg-names = "mem";
++ };
++
+ clint: clint@2000000 {
+ compatible = "starfive,jh7100-clint", "sifive,clint0";
+ reg = <0x0 0x2000000 0x0 0x10000>;
+@@ -172,6 +199,151 @@
+ riscv,ndev = <133>;
+ };
+
++ gmac: ethernet@10020000 {
++ compatible = "starfive,jh7100-dwmac", "snps,dwmac";
++ reg = <0x0 0x10020000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_GMAC_ROOT_DIV>,
++ <&clkgen JH7100_CLK_GMAC_AHB>,
++ <&clkgen JH7100_CLK_GMAC_PTP_REF>,
++ <&clkgen JH7100_CLK_GMAC_TX_INV>,
++ <&clkgen JH7100_CLK_GMAC_GTX>;
++ clock-names = "stmmaceth", "pclk", "ptp_ref", "tx", "gtx";
++ resets = <&rstgen JH7100_RSTN_GMAC_AHB>;
++ reset-names = "ahb";
++ interrupts = <6>, <7>;
++ interrupt-names = "macirq", "eth_wake_irq";
++ max-frame-size = <9000>;
++ snps,multicast-filter-bins = <0>;
++ snps,perfect-filter-entries = <128>;
++ starfive,syscon = <&sysmain 0x70 0>;
++ rx-fifo-depth = <32768>;
++ tx-fifo-depth = <16384>;
++ snps,axi-config = <&stmmac_axi_setup>;
++ snps,fixed-burst;
++ /*snps,force_sf_dma_mode;*/
++ snps,force_thresh_dma_mode;
++ snps,no-pbl-x8;
++ status = "disabled";
++ };
++
++ dma2p: dma-controller@100b0000 {
++ compatible = "starfive,jh7100-axi-dma";
++ reg = <0x0 0x100b0000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SGDMA2P_AXI>,
++ <&clkgen JH7100_CLK_SGDMA2P_AHB>;
++ clock-names = "core-clk", "cfgr-clk";
++ resets = <&rstgen JH7100_RSTN_SGDMA2P_AXI>,
++ <&rstgen JH7100_RSTN_SGDMA2P_AHB>;
++ reset-names = "axi", "ahb";
++ interrupts = <2>;
++ #dma-cells = <1>;
++ dma-channels = <4>;
++ snps,dma-masters = <1>;
++ snps,data-width = <4>;
++ snps,block-size = <4096 4096 4096 4096>;
++ snps,priority = <0 1 2 3>;
++ snps,axi-max-burst-len = <128>;
++ dma-coherent;
++ };
++
++ crypto: crypto@100d0000 {
++ compatible = "starfive,vic-sec";
++ reg = <0x0 0x100d0000 0x0 0x20000>,
++ <0x0 0x11800234 0x0 0xc>;
++ reg-names = "secmem", "secclk";
++ clocks = <&clkgen JH7100_CLK_SEC_AHB>;
++ interrupts = <31>;
++ };
++
++ i2sadc0: i2sadc0@10400000 {
++ compatible = "snps,designware-i2sadc0";
++ reg = <0x0 0x10400000 0x0 0x1000>;
++ clocks = <&clkgen JH7100_CLK_APB1_BUS>;
++ clock-names = "i2sclk";
++ interrupt-parent = <&plic>;
++ #sound-dai-cells = <0>;
++ dmas = <&dma2p 28>;
++ dma-names = "rx";
++ };
++
++ i2svad: i2svad@10420000 {
++ compatible = "starfive,sf-i2svad";
++ reg = <0x0 0x10420000 0x0 0x1000> ;
++ clocks = <&audclk JH7100_AUDCLK_I2SVAD_APB>;
++ clock-names = "i2svad_apb";
++ resets = <&audrst JH7100_AUDRSTN_I2SVAD_APB>,
++ <&audrst JH7100_AUDRSTN_I2SVAD_SRST>;
++ reset-names = "apb_i2svad", "i2svad_srst";
++ interrupts = <60>, <61>;
++ interrupt-names = "spintr", "slintr";
++ #sound-dai-cells = <0>;
++ };
++
++ pwmdac: pwmdac@10440000 {
++ compatible = "starfive,pwmdac";
++ reg = <0x0 0x10440000 0x0 0x1000>;
++ clocks = <&clkgen JH7100_CLK_AUDIO_ROOT>,
++ <&clkgen JH7100_CLK_AUDIO_SRC>,
++ <&clkgen JH7100_CLK_AUDIO_12288>,
++ <&audclk JH7100_AUDCLK_DMA1P_AHB>,
++ <&audclk JH7100_AUDCLK_PWMDAC_APB>,
++ <&audclk JH7100_AUDCLK_DAC_MCLK>;
++ clock-names = "audio_root",
++ "audio_src",
++ "audio_12288",
++ "dma1p_ahb",
++ "pwmdac_apb",
++ "dac_mclk";
++ resets = <&audrst JH7100_AUDRSTN_APB_BUS>,
++ <&audrst JH7100_AUDRSTN_DMA1P_AHB>,
++ <&audrst JH7100_AUDRSTN_PWMDAC_APB>;
++ reset-names = "apb_bus", "dma1p_ahb", "apb_pwmdac";
++ dmas = <&dma2p 23>;
++ dma-names = "tx";
++ #sound-dai-cells = <0>;
++ };
++
++ i2sdac0: i2sdac0@10450000 {
++ compatible = "snps,designware-i2sdac0";
++ reg = <0x0 0x10450000 0x0 0x1000>;
++ clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>,
++ <&audclk JH7100_AUDCLK_I2SDAC_BCLK>,
++ <&audclk JH7100_AUDCLK_I2SDAC_LRCLK>,
++ <&audclk JH7100_AUDCLK_I2SDAC_APB>;
++ clock-names = "dac_mclk", "i2sdac0_bclk", "i2sdac0_lrclk", "i2sdac_apb";
++ resets = <&audrst JH7100_AUDRSTN_I2SDAC_APB>,
++ <&audrst JH7100_AUDRSTN_I2SDAC_SRST>;
++ reset-names = "apb_i2sdac", "i2sdac_srst";
++ #sound-dai-cells = <0>;
++ dmas = <&dma2p 30>;
++ dma-names = "tx";
++ };
++
++ i2sdac1: i2sdac1@10460000 {
++ compatible = "snps,designware-i2sdac1";
++ reg = <0x0 0x10460000 0x0 0x1000>;
++ clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>,
++ <&audclk JH7100_AUDCLK_I2S1_BCLK>,
++ <&audclk JH7100_AUDCLK_I2S1_LRCLK>,
++ <&audclk JH7100_AUDCLK_I2S1_APB>;
++ clock-names = "dac_mclk", "i2sdac1_bclk", "i2sdac1_lrclk", "i2s1_apb";
++ resets = <&audrst JH7100_AUDRSTN_I2S1_APB>,
++ <&audrst JH7100_AUDRSTN_I2S1_SRST>;
++ #sound-dai-cells = <0>;
++ dmas = <&dma2p 31>;
++ dma-names = "tx";
++ };
++
++ i2sdac16k: i2sdac16k@10470000 {
++ compatible = "snps,designware-i2sdac16k";
++ reg = <0x0 0x10470000 0x0 0x1000>;
++ clocks = <&clkgen JH7100_CLK_APB1_BUS>;
++ clock-names = "i2sclk";
++ #sound-dai-cells = <0>;
++ dmas = <&dma2p 29>;
++ dma-names = "tx";
++ };
++
+ audclk: clock-controller@10480000 {
+ compatible = "starfive,jh7100-audclk";
+ reg = <0x0 0x10480000 0x0 0x10000>;
+@@ -214,6 +386,82 @@
+ status = "disabled";
+ };
+
++ spdif_transmitter: spdif-transmitter {
++ compatible = "linux,spdif-dit";
++ #sound-dai-cells = <0>;
++ };
++
++ spdif_receiver: spdif-receiver {
++ compatible = "linux,spdif-dir";
++ #sound-dai-cells = <0>;
++ };
++
++ pwmdac_codec: pwmdac-transmitter {
++ compatible = "linux,pwmdac-dit";
++ #sound-dai-cells = <0>;
++ };
++
++ dmic_codec: dmic {
++ compatible = "dmic-codec";
++ #sound-dai-cells = <0>;
++ };
++
++ sound: snd-card {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "Starfive-Multi-Sound-Card";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ /* pwmdac */
++ simple-audio-card,dai-link@0 {
++ reg = <0>;
++ status = "okay";
++ format = "left_j";
++ bitclock-master = <&sndcpu0>;
++ frame-master = <&sndcpu0>;
++
++ sndcpu0: cpu {
++ sound-dai = <&pwmdac>;
++ };
++
++ codec {
++ sound-dai = <&pwmdac_codec>;
++ };
++ };
++ };
++
++ usb3: usb@104c0000 {
++ compatible = "cdns,usb3";
++ reg = <0x0 0x104c0000 0x0 0x10000>, // memory area for HOST registers
++ <0x0 0x104d0000 0x0 0x10000>, // memory area for DEVICE registers
++ <0x0 0x104e0000 0x0 0x10000>; // memory area for OTG/DRD registers
++ reg-names = "otg", "xhci", "dev";
++ interrupts = <44>, <52>, <43>;
++ interrupt-names = "host", "peripheral", "otg";
++ phy-names = "cdns3,usb3-phy", "cdns3,usb2-phy";
++ maximum-speed = "super-speed";
++ status = "disabled";
++ };
++
++ dma1p: dma-controller@10500000 {
++ compatible = "starfive,jh7100-axi-dma";
++ reg = <0x0 0x10500000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SGDMA1P_AXI>,
++ <&clkgen JH7100_CLK_SGDMA1P_BUS>;
++ clock-names = "core-clk", "cfgr-clk";
++ resets = <&rstgen JH7100_RSTN_DMA1P_AXI>,
++ <&rstgen JH7100_RSTN_SGDMA1P_AXI>;
++ reset-names = "axi", "ahb";
++ interrupts = <1>;
++ #dma-cells = <1>;
++ dma-channels = <16>;
++ snps,dma-masters = <1>;
++ snps,data-width = <3>;
++ snps,block-size = <4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096>;
++ snps,priority = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>;
++ snps,axi-max-burst-len = <64>;
++ };
++
+ clkgen: clock-controller@11800000 {
+ compatible = "starfive,jh7100-clkgen";
+ reg = <0x0 0x11800000 0x0 0x10000>;
+@@ -222,12 +470,93 @@
+ #clock-cells = <1>;
+ };
+
++ otp: otp@11810000 {
++ compatible = "starfive,fu740-otp";
++ reg = <0x0 0x11810000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_OTP_APB>;
++ fuse-count = <0x200>;
++ };
++
+ rstgen: reset-controller@11840000 {
+ compatible = "starfive,jh7100-reset";
+ reg = <0x0 0x11840000 0x0 0x10000>;
+ #reset-cells = <1>;
+ };
+
++ sysmain: syscon@11850000 {
++ compatible = "starfive,jh7100-sysmain", "syscon";
++ reg = <0x0 0x11850000 0x0 0x10000>;
++ };
++
++ qspi: spi@11860000 {
++ compatible = "cdns,qspi-nor";
++ reg = <0x0 0x11860000 0x0 0x10000>,
++ <0x0 0x20000000 0x0 0x20000000>;
++ clocks = <&clkgen JH7100_CLK_QSPI_AHB>;
++ interrupts = <3>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ cdns,fifo-depth = <256>;
++ cdns,fifo-width = <4>;
++ cdns,trigger-address = <0x0>;
++ spi-max-frequency = <250000000>;
++ status = "disabled";
++ };
++
++ uart0: serial@11870000 {
++ compatible = "starfive,jh7100-hsuart", "snps,dw-apb-uart";
++ reg = <0x0 0x11870000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_UART0_CORE>,
++ <&clkgen JH7100_CLK_UART0_APB>;
++ clock-names = "baudclk", "apb_pclk";
++ resets = <&rstgen JH7100_RSTN_UART0_APB>;
++ interrupts = <92>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ status = "disabled";
++ };
++
++ uart1: serial@11880000 {
++ compatible = "starfive,jh7100-hsuart", "snps,dw-apb-uart";
++ reg = <0x0 0x11880000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_UART1_CORE>,
++ <&clkgen JH7100_CLK_UART1_APB>;
++ clock-names = "baudclk", "apb_pclk";
++ resets = <&rstgen JH7100_RSTN_UART1_APB>;
++ interrupts = <93>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ status = "disabled";
++ };
++
++ spi0: spi@11890000 {
++ compatible = "snps,dw-apb-ssi";
++ reg = <0x0 0x11890000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SPI0_CORE>,
++ <&clkgen JH7100_CLK_SPI0_APB>;
++ clock-names = "ssi_clk", "pclk";
++ resets = <&rstgen JH7100_RSTN_SPI0_APB>;
++ reset-names = "spi";
++ interrupts = <94>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi1: spi@118a0000 {
++ compatible = "snps,dw-apb-ssi";
++ reg = <0x0 0x118a0000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SPI1_CORE>,
++ <&clkgen JH7100_CLK_SPI1_APB>;
++ clock-names = "ssi_clk", "pclk";
++ resets = <&rstgen JH7100_RSTN_SPI1_APB>;
++ reset-names = "spi";
++ interrupts = <95>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
+ i2c0: i2c@118b0000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x0 0x118b0000 0x0 0x10000>;
+@@ -254,6 +583,41 @@
+ status = "disabled";
+ };
+
++ trng: trng@118d0000 {
++ compatible = "starfive,vic-rng";
++ reg = <0x0 0x118d0000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_TRNG_APB>;
++ interrupts = <98>;
++ };
++
++ vpu_enc: vpu_enc@118e0000 {
++ compatible = "cm,cm521-vpu";
++ reg = <0x0 0x118e0000 0x0 0x4000>;
++ reg-names = "control";
++ clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++ clock-names = "vcodec";
++ interrupts = <26>;
++ };
++
++ vpu_dec: vpu_dec@118f0000 {
++ compatible = "c&m,cm511-vpu";
++ reg = <0 0x118f0000 0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++ clock-names = "vcodec";
++ interrupts = <23>;
++ //memory-region = <&vpu_reserved>;
++ };
++
++ jpu: coadj12@11900000 {
++ compatible = "cm,codaj12-jpu-1";
++ reg = <0x0 0x11900000 0x0 0x300>;
++ reg-names = "control";
++ clocks = <&clkgen JH7100_CLK_JPEG_APB>;
++ clock-names = "jpege";
++ interrupts = <24>;
++ memory-region = <&jpu_reserved>;
++ };
++
+ gpio: pinctrl@11910000 {
+ compatible = "starfive,jh7100-pinctrl";
+ reg = <0x0 0x11910000 0x0 0x10000>,
+@@ -268,6 +632,86 @@
+ #interrupt-cells = <2>;
+ };
+
++ nvdla@11940000 {
++ compatible = "nvidia,nvdla_os_initial";
++ interrupts = <22>;
++ memory-region = <&nvdla_reserved>;
++ reg = <0x0 0x11940000 0x0 0x40000>;
++ status = "okay";
++ };
++
++ display: display-subsystem {
++ compatible = "starfive,display-subsystem";
++ dma-coherent;
++ status = "disabled";
++ };
++
++ encoder: display-encoder {
++ compatible = "starfive,display-encoder";
++ status = "disabled";
++ };
++
++ crtc: crtc@12000000 {
++ compatible = "starfive,jh7100-crtc";
++ reg = <0x0 0x12000000 0x0 0x10000>,
++ <0x0 0x12040000 0x0 0x10000>,
++ <0x0 0x12080000 0x0 0x10000>,
++ <0x0 0x120c0000 0x0 0x10000>,
++ <0x0 0x12240000 0x0 0x10000>,
++ <0x0 0x12250000 0x0 0x10000>,
++ <0x0 0x12260000 0x0 0x10000>;
++ reg-names = "lcdc", "vpp0", "vpp1", "vpp2", "clk", "rst", "sys";
++ clocks = <&clkgen JH7100_CLK_DISP_AXI>, <&clkgen JH7100_CLK_VOUT_SRC>;
++ clock-names = "disp_axi", "vout_src";
++ resets = <&rstgen JH7100_RSTN_DISP_AXI>, <&rstgen JH7100_RSTN_VOUT_SRC>;
++ reset-names = "disp_axi", "vout_src";
++ interrupts = <101>, <103>;
++ interrupt-names = "lcdc_irq", "vpp1_irq";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++
++ pp1 {
++ pp-id = <1>;
++ fifo-out;
++ //sys-bus-out;
++ src-format = <11>; //<COLOR_RGB565>;
++ src-width = <1920>;
++ src-height = <1080>;
++ dst-format = <7>; //<COLOR_RGB888_ARGB>;
++ dst-width = <1920>;
++ dst-height = <1080>;
++ };
++ };
++
++ spi2: spi@12410000 {
++ compatible = "snps,dw-apb-ssi";
++ reg = <0x0 0x12410000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SPI2_CORE>,
++ <&clkgen JH7100_CLK_SPI2_APB>;
++ clock-names = "ssi_clk", "pclk";
++ resets = <&rstgen JH7100_RSTN_SPI2_APB>;
++ reset-names = "spi";
++ interrupts = <70>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi3: spi@12420000 {
++ compatible = "snps,dw-apb-ssi";
++ reg = <0x0 0x12420000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_SPI3_CORE>,
++ <&clkgen JH7100_CLK_SPI3_APB>;
++ clock-names = "ssi_clk", "pclk";
++ resets = <&rstgen JH7100_RSTN_SPI3_APB>;
++ reset-names = "spi";
++ interrupts = <71>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
+ uart2: serial@12430000 {
+ compatible = "starfive,jh7100-uart", "snps,dw-apb-uart";
+ reg = <0x0 0x12430000 0x0 0x10000>;
+@@ -341,5 +785,64 @@
+ reset-names = "sense", "bus";
+ #thermal-sensor-cells = <0>;
+ };
++
++ ptc: pwm@12490000 {
++ compatible = "starfive,pwm0";
++ reg = <0x0 0x12490000 0x0 0x10000>;
++ clocks = <&clkgen JH7100_CLK_PWM_APB>;
++ resets = <&rstgen JH7100_RSTN_PWM_APB>;
++ #pwm-cells = <3>;
++ sifive,npwm = <8>;
++ status = "disabled";
++ };
++
++ thermal-zones {
++ cpu-thermal {
++ polling-delay-passive = <250>;
++ polling-delay = <15000>;
++
++ thermal-sensors = <&sfctemp>;
++
++ cooling-maps {
++ };
++
++ trips {
++ cpu_alert0: cpu_alert0 {
++ /* milliCelsius */
++ temperature = <75000>;
++ hysteresis = <2000>;
++ type = "passive";
++ };
++
++ cpu_crit: cpu_crit {
++ /* milliCelsius */
++ temperature = <90000>;
++ hysteresis = <2000>;
++ type = "critical";
++ };
++ };
++ };
++ };
++
++ xrp@f0000000 {
++ compatible = "cdns,xrp";
++ reg = <0x0 0xf0000000 0x0 0x01ffffff>,
++ <0x10 0x72000000 0x0 0x00001000>,
++ <0x10 0x72001000 0x0 0x00fff000>,
++ <0x0 0x124b0000 0x0 0x00010000>;
++ clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++ interrupts = <27>, <28>;
++ firmware-name = "vp6_elf";
++ dsp-irq = <19 20>;
++ dsp-irq-src = <0x20 0x21>;
++ intc-irq-mode = <1>;
++ intc-irq = <0 1>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x40000000 0x0 0x40000000 0x01000000>,
++ <0xb0000000 0x10 0x70000000 0x3000000>;
++ dsp@0 {
++ };
++ };
+ };
+ };