* [PATCH v2 4/5] regulator: rk808: Support apply_bit for rk808_set_suspend_voltage_range
From: Alex Bee @ 2024-03-23 8:58 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Linus Walleij, Liam Girdwood, Mark Brown
Cc: Chris Zhong, Zhang Qing, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, linux-gpio, Alex Bee
In-Reply-To: <20240323085852.116756-1-knaerzche@gmail.com>
rk808_set_suspend_voltage_range currently does not account the existence of
apply_bit/apply_reg.
This adds support for those in same way it is done in
regulator_set_voltage_sel_regmap and is required for the upcoming RK816
support
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
changes since v1:
- none
drivers/regulator/rk808-regulator.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index d89ae7f16d7a..a6a563e402d0 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -534,15 +534,25 @@ static int rk808_set_suspend_voltage_range(struct regulator_dev *rdev, int uv)
{
unsigned int reg;
int sel = regulator_map_voltage_linear_range(rdev, uv, uv);
+ int ret;
if (sel < 0)
return -EINVAL;
reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
- return regmap_update_bits(rdev->regmap, reg,
- rdev->desc->vsel_mask,
- sel);
+ ret = regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->vsel_mask,
+ sel);
+ if (ret)
+ return ret;
+
+ if (rdev->desc->apply_bit)
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
+ rdev->desc->apply_bit,
+ rdev->desc->apply_bit);
+
+ return ret;
}
static int rk805_set_suspend_enable(struct regulator_dev *rdev)
--
2.43.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 3/5] pinctrl: rk805: Add rk816 pinctrl support
From: Alex Bee @ 2024-03-23 8:58 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Linus Walleij, Liam Girdwood, Mark Brown
Cc: Chris Zhong, Zhang Qing, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, linux-gpio, Alex Bee
In-Reply-To: <20240323085852.116756-1-knaerzche@gmail.com>
This adds support for RK816 to the exising rk805 pinctrl driver
It has a single pin which can be configured as input from a thermistor (for
instance in an attached battery) or as a gpio.
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
changes since v1:
- rename pin-function names according to the updated binding
- added missing fun_reg for rk816_gpio_cfgs
drivers/pinctrl/pinctrl-rk805.c | 69 +++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/drivers/pinctrl/pinctrl-rk805.c b/drivers/pinctrl/pinctrl-rk805.c
index 56d916f2cee6..4f1a6d8fcda0 100644
--- a/drivers/pinctrl/pinctrl-rk805.c
+++ b/drivers/pinctrl/pinctrl-rk805.c
@@ -93,6 +93,11 @@ enum rk806_pinmux_option {
RK806_PINMUX_FUN5,
};
+enum rk816_pinmux_option {
+ RK816_PINMUX_THERMISTOR,
+ RK816_PINMUX_GPIO,
+};
+
enum {
RK805_GPIO0,
RK805_GPIO1,
@@ -104,6 +109,10 @@ enum {
RK806_GPIO_DVS3
};
+enum {
+ RK816_GPIO0,
+};
+
static const char *const rk805_gpio_groups[] = {
"gpio0",
"gpio1",
@@ -115,6 +124,10 @@ static const char *const rk806_gpio_groups[] = {
"gpio_pwrctrl3",
};
+static const char *const rk816_gpio_groups[] = {
+ "gpio0",
+};
+
/* RK805: 2 output only GPIOs */
static const struct pinctrl_pin_desc rk805_pins_desc[] = {
PINCTRL_PIN(RK805_GPIO0, "gpio0"),
@@ -128,6 +141,11 @@ static const struct pinctrl_pin_desc rk806_pins_desc[] = {
PINCTRL_PIN(RK806_GPIO_DVS3, "gpio_pwrctrl3"),
};
+/* RK816 */
+static const struct pinctrl_pin_desc rk816_pins_desc[] = {
+ PINCTRL_PIN(RK816_GPIO0, "gpio0"),
+};
+
static const struct rk805_pin_function rk805_pin_functions[] = {
{
.name = "gpio",
@@ -176,6 +194,21 @@ static const struct rk805_pin_function rk806_pin_functions[] = {
},
};
+static const struct rk805_pin_function rk816_pin_functions[] = {
+ {
+ .name = "pin_fun_gpio",
+ .groups = rk816_gpio_groups,
+ .ngroups = ARRAY_SIZE(rk816_gpio_groups),
+ .mux_option = RK816_PINMUX_GPIO,
+ },
+ {
+ .name = "pin_fun_thermistor",
+ .groups = rk816_gpio_groups,
+ .ngroups = ARRAY_SIZE(rk816_gpio_groups),
+ .mux_option = RK816_PINMUX_THERMISTOR,
+ },
+};
+
static const struct rk805_pin_group rk805_pin_groups[] = {
{
.name = "gpio0",
@@ -207,6 +240,14 @@ static const struct rk805_pin_group rk806_pin_groups[] = {
}
};
+static const struct rk805_pin_group rk816_pin_groups[] = {
+ {
+ .name = "gpio0",
+ .pins = { RK816_GPIO0 },
+ .npins = 1,
+ },
+};
+
#define RK805_GPIO0_VAL_MSK BIT(0)
#define RK805_GPIO1_VAL_MSK BIT(1)
@@ -255,6 +296,20 @@ static struct rk805_pin_config rk806_gpio_cfgs[] = {
}
};
+#define RK816_FUN_MASK BIT(2)
+#define RK816_VAL_MASK BIT(3)
+#define RK816_DIR_MASK BIT(4)
+
+static struct rk805_pin_config rk816_gpio_cfgs[] = {
+ {
+ .fun_reg = RK818_IO_POL_REG,
+ .fun_msk = RK816_FUN_MASK,
+ .reg = RK818_IO_POL_REG,
+ .val_msk = RK816_VAL_MASK,
+ .dir_msk = RK816_DIR_MASK,
+ },
+};
+
/* generic gpio chip */
static int rk805_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
@@ -439,6 +494,8 @@ static int rk805_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
return _rk805_pinctrl_set_mux(pctldev, offset, RK805_PINMUX_GPIO);
case RK806_ID:
return _rk805_pinctrl_set_mux(pctldev, offset, RK806_PINMUX_FUN5);
+ case RK816_ID:
+ return _rk805_pinctrl_set_mux(pctldev, offset, RK816_PINMUX_GPIO);
}
return -ENOTSUPP;
@@ -588,6 +645,18 @@ static int rk805_pinctrl_probe(struct platform_device *pdev)
pci->pin_cfg = rk806_gpio_cfgs;
pci->gpio_chip.ngpio = ARRAY_SIZE(rk806_gpio_cfgs);
break;
+ case RK816_ID:
+ pci->pins = rk816_pins_desc;
+ pci->num_pins = ARRAY_SIZE(rk816_pins_desc);
+ pci->functions = rk816_pin_functions;
+ pci->num_functions = ARRAY_SIZE(rk816_pin_functions);
+ pci->groups = rk816_pin_groups;
+ pci->num_pin_groups = ARRAY_SIZE(rk816_pin_groups);
+ pci->pinctrl_desc.pins = rk816_pins_desc;
+ pci->pinctrl_desc.npins = ARRAY_SIZE(rk816_pins_desc);
+ pci->pin_cfg = rk816_gpio_cfgs;
+ pci->gpio_chip.ngpio = ARRAY_SIZE(rk816_gpio_cfgs);
+ break;
default:
dev_err(&pdev->dev, "unsupported RK805 ID %lu\n",
pci->rk808->variant);
--
2.43.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 0/5] Add RK816 PMIC support
From: Alex Bee @ 2024-03-23 8:58 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Linus Walleij, Liam Girdwood, Mark Brown
Cc: Chris Zhong, Zhang Qing, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, linux-gpio, Alex Bee
This series aims to add support for Rockchip RK816 PMIC series. As per
datasheet it's targeted for RK3126/RK3128 (RK816-1), RK1108 (RK816-2) and
PX3-SE (RK816-3) but might be used for other SoCs as well. The MFD consists
of an integrated RTC, a GPIO controller, two 32k clock outputs, a power
key, 3 buck- and 6 ldo regulators, 3 regulator-switches, and charger with
integrated fuel gauge. Charger and fuel gauge are not part of this series.
Two of the switches (otg/boost) are part of the binding, but not of
the driver. They must only ever be enabled if no battery charging is
happening, but it will be enabled automatically if a battery is attached
and an external power source is connected. Thus that needs some
incorporation of a yet to be added charger driver.
Integration in the existing rk8xx-infrastructure was pretty straightforward
and only needed very little tweaking. In order to not further bloat the
driver(s) too much with additional `#define`s I tried to re-use existing
ones wherever possible.
The patches are loosely based on the vendor's implementation, verified
against the datasheet and tested/measured on a RK3126 board. As they are
touching several subsystems I'm sending them (very) early for the
6.10.-cycle.
changes since v1:
- integrated Krzysztof's feedback for the bindings and the resulting
driver changes
- fixed a sparse warning
link to v1:
https://lore.kernel.org/lkml/20240321143911.90210-2-knaerzche@gmail.com/
Please see individual patches for details about the changes.
Alex Bee (5):
dt-bindings: mfd: Add rk816 binding
mfd: rk8xx: Add RK816 support
pinctrl: rk805: Add rk816 pinctrl support
regulator: rk808: Support apply_bit for
rk808_set_suspend_voltage_range
regulator: rk808: Add RK816 support
.../bindings/mfd/rockchip,rk816.yaml | 269 ++++++++++++++++++
drivers/mfd/Kconfig | 4 +-
drivers/mfd/rk8xx-core.c | 103 +++++++
drivers/mfd/rk8xx-i2c.c | 45 ++-
drivers/pinctrl/pinctrl-rk805.c | 69 +++++
drivers/regulator/rk808-regulator.c | 218 +++++++++++++-
include/linux/mfd/rk808.h | 141 +++++++++
7 files changed, 842 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/rockchip,rk816.yaml
--
2.43.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 5/8] drm: zynqmp_dp: Don't retrain the link in our IRQ
From: Tomi Valkeinen @ 2024-03-23 8:54 UTC (permalink / raw)
To: Sean Anderson
Cc: Michal Simek, David Airlie, linux-kernel, Daniel Vetter,
linux-arm-kernel, Laurent Pinchart, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, dri-devel
In-Reply-To: <7a4c332b-a044-4c82-a5b2-cb4b318f5110@linux.dev>
On 22/03/2024 23:22, Sean Anderson wrote:
> On 3/22/24 14:09, Tomi Valkeinen wrote:
>> On 22/03/2024 18:18, Sean Anderson wrote:
>>> On 3/22/24 01:32, Tomi Valkeinen wrote:
>>>> On 21/03/2024 21:17, Sean Anderson wrote:
>>>>> On 3/21/24 15:08, Tomi Valkeinen wrote:
>>>>>> On 21/03/2024 20:01, Sean Anderson wrote:
>>>>>>> On 3/21/24 13:25, Tomi Valkeinen wrote:
>>>>>>>> On 21/03/2024 17:52, Sean Anderson wrote:
>>>>>>>>> On 3/20/24 02:53, Tomi Valkeinen wrote:
>>>>>>>>>> On 20/03/2024 00:51, Sean Anderson wrote:
>>>>>>>>>> Do we need to handle interrupts while either delayed work is being done?
>>>>>>>>>
>>>>>>>>> Probably not.
>>>>>>>>>
>>>>>>>>>> If we do need a delayed work, would just one work be enough which
>>>>>>>>>> handles both HPD_EVENT and HPD_IRQ, instead of two?
>>>>>>>>>
>>>>>>>>> Maybe, but then we need to determine which pending events we need to
>>>>>>>>> handle. I think since we have only two events it will be easier to just
>>>>>>>>> have separate workqueues.
>>>>>>>>
>>>>>>>> The less concurrency, the better...Which is why it would be nice to do it all in the threaded irq.
>>>>>>>
>>>>>>> Yeah, but we can use a mutex for this which means there is not too much
>>>>>>> interesting going on.
>>>>>>
>>>>>> Ok. Yep, if we get (hopefully) a single mutex with clearly defined fields that it protects, I'm ok with workqueues.
>>>>>>
>>>>>> I'd still prefer just one workqueue, though...
>>>>>
>>>>> Yeah, but then we need a spinlock or something to tell the workqueue what it should do.
>>>>
>>>> Yep. We could also always look at the HPD (if we drop the big sleeps) in the wq, and have a flag for the HPD IRQ, which would reduce the state to a single bit.
>>>
>>> How about something like
>>>
>>> zynqmp_dp_irq_handler(...)
>>> {
>>> /* Read status and handle underflow/overflow/vblank */
>>>
>>> status &= ZYNQMP_DP_INT_HPD_EVENT | ZYNQMP_DP_INT_HPD_IRQ;
>>> if (status) {
>>> atomic_or(status, &dp->status);
>>> return IRQ_WAKE_THREAD;
>>> }
>>>
>>> return IRQ_HANDLED;
>>> }
>>>
>>> zynqmp_dp_thread_handler(...)
>>> {
>>> status = atomic_xchg(&dp->status, 0);
>>> /* process HPD stuff */
>>> }
>>>
>>> which gets rid of the workqueue too.
>>
>> I like it. We can't use IRQF_ONESHOT, as that would keep the irq masked while the threaded handler is being ran. I don't think that's a problem, but just something to keep in mind that both handlers can run concurrently.
>
> Actually, I'm not sure we can do it like this. Imagine we have something
> like
>
> CPU 0 CPU 1
> zynqmp_dp_thread_handler()
> atomic_xchg()
> __handle_irq_event_percpu
> zynqmp_dp_irq_handler()
> atomic_or()
> return IRQ_WAIT_THREAD
> __irq_wake_thread()
> test_and_set_bit(IRQTF_RUNTHREAD, ...)
> return
> return IRQ_HANDLED
>
> and whoops we now have bits set in dp->status but the thread isn't
> running. I don't think there's a way to fix this without locking (or two
In your example above, the IRQTF_RUNTHREAD has been cleared by the
threaded-irq before calling zynqmp_dp_thread_handler. So the hard-irq
will set that flag before the zynqmp_dp_thread_handler() returns.
When zynqmp_dp_thread_handler() returns, the execution will go to
irq_wait_for_interrupt(). That function will notice the IRQTF_RUNTHREAD
flag (and clear it), and run the zynqmp_dp_thread_handler() again.
So if I'm not mistaken, when the hard-irq function returns
IRQ_WAKE_THREAD, it's always guaranteed that a "fresh" run of the
threaded handler will be ran.
I think that makes sense, as I'm not sure how threaded handlers without
IRQF_ONESHOT could be used if that were not the case. I hope I'm right
in my analysis =).
Tomi
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/5] mfd: rk8xx: Add RK816 support
From: kernel test robot @ 2024-03-23 7:01 UTC (permalink / raw)
To: Alex Bee, Lee Jones, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Linus Walleij, Liam Girdwood,
Mark Brown
Cc: oe-kbuild-all, Chris Zhong, Zhang Qing, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel, linux-gpio,
Alex Bee
In-Reply-To: <20240321143911.90210-5-knaerzche@gmail.com>
Hi Alex,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-mfd/for-mfd-next]
[also build test WARNING on lee-mfd/for-mfd-fixes broonie-regulator/for-next robh/for-next linus/master v6.8 next-20240322]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Alex-Bee/dt-bindings-mfd-Add-rk816-binding/20240321-224318
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link: https://lore.kernel.org/r/20240321143911.90210-5-knaerzche%40gmail.com
patch subject: [PATCH 2/5] mfd: rk8xx: Add RK816 support
config: arc-randconfig-001-20240322 (https://download.01.org/0day-ci/archive/20240323/202403231417.oV6q6CGc-lkp@intel.com/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240323/202403231417.oV6q6CGc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202403231417.oV6q6CGc-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/mfd/rk8xx-core.c:395:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
395 | static const unsigned int rk816_get_irq_reg(struct regmap_irq_chip_data *data,
| ^~~~~
vim +395 drivers/mfd/rk8xx-core.c
394
> 395 static const unsigned int rk816_get_irq_reg(struct regmap_irq_chip_data *data,
396 unsigned int base, int index)
397 {
398 unsigned int irq_reg = base;
399
400 switch (base) {
401 case RK816_INT_STS_REG1:
402 irq_reg += rk816_irq_status_offsets[index];
403 break;
404 case RK816_INT_STS_MSK_REG1:
405 irq_reg += rk816_irq_mask_offsets[index];
406 break;
407 }
408
409 return irq_reg;
410 };
411
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Kent Overstreet @ 2024-03-23 4:16 UTC (permalink / raw)
To: Boqun Feng
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <Zf5Rp0zR_fyZMADn@Boquns-Mac-mini.home>
On Fri, Mar 22, 2024 at 08:51:03PM -0700, Boqun Feng wrote:
> On Fri, Mar 22, 2024 at 11:10:36PM -0400, Kent Overstreet wrote:
> > On Fri, Mar 22, 2024 at 07:57:20PM -0700, Boqun Feng wrote:
> > > On Fri, Mar 22, 2024 at 10:33:13PM -0400, Kent Overstreet wrote:
> > > > On Fri, Mar 22, 2024 at 07:26:28PM -0700, Boqun Feng wrote:
> > > > > On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
> > > > > [...]
> > > > > > > Boqun already mentioned the "mixing access sizes", which is actually
> > > > > > > quite fundamental in the kernel, where we play lots of games with that
> > > > > > > (typically around locking, where you find patterns line unlock writing
> > > > > > > a zero to a single byte, even though the whole lock data structure is
> > > > > > > a word). And sometimes the access size games are very explicit (eg
> > > > > > > lib/lockref.c).
> > > > > >
> > > > > > I don't think mixing access sizes should be a real barrier. On the read
> > > > >
> > > > > Well, it actually is, since mixing access sizes is, guess what,
> > > > > an undefined behavior:
> > > > >
> > > > > (example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
> > > > >
> > > > > thread::scope(|s| {
> > > > > // This is UB: using different-sized atomic accesses to the same data
> > > > > s.spawn(|| atomic.store(1, Ordering::Relaxed));
> > > > > s.spawn(|| unsafe {
> > > > > let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
> > > > > differently_sized.store(2, Ordering::Relaxed);
> > > > > });
> > > > > });
> > > > >
> > > > > Of course, you can say "I will just ignore the UB", but if you have to
> > > > > ignore "compiler rules" to make your code work, why bother use compiler
> > > > > builtin in the first place? Being UB means they are NOT guaranteed to
> > > > > work.
> > > >
> > > > That's not what I'm proposing - you'd need additional compiler support.
> > >
> > > Ah, OK.
> > >
> > > > but the new intrinsic would be no different, semantics wise for the
> > > > compiler to model, than a "lock orb".
> > >
> > > Be ready to be disappointed:
> > >
> > > https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402078545
> > > https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402082631
> > >
> > > ;-)
> > >
> > > In fact, if you get a chance to read the previous discussion links I
> > > shared, you will find I was just like you in the beginning: hope we
> > > could extend the model to support more kernel code properly. But my
> > > overall feeling is that it's either very challenging or lack of
> > > motivation to do.
> >
> > That's casting - that doesn't work because compiler people hate
> > aliasing.
> >
> > But intrinsics for e.g.
> > __atomic32_read_u8(atomic_u32_t *a, unsigned byte)
> > __atomic32_write_u8(atomic_u32_t a*, unsigned byte)
> >
>
> so "byte" here is the byte indexing in the u32? Hmm... I guess that'll
> work. But I really don't know whether LLVM/Rust will support such an
> intrinsic...
They're going to need this eventually - really, entire structs that can
be marked as atomic. Types aren't limited to the integers.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Boqun Feng @ 2024-03-23 3:51 UTC (permalink / raw)
To: Kent Overstreet
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <qsw2v5ikt2w6m2xfr6h4e2xauobhy37nrskarlfjro4ek4qw4b@jgxhav7bia55>
On Fri, Mar 22, 2024 at 11:10:36PM -0400, Kent Overstreet wrote:
> On Fri, Mar 22, 2024 at 07:57:20PM -0700, Boqun Feng wrote:
> > On Fri, Mar 22, 2024 at 10:33:13PM -0400, Kent Overstreet wrote:
> > > On Fri, Mar 22, 2024 at 07:26:28PM -0700, Boqun Feng wrote:
> > > > On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
> > > > [...]
> > > > > > Boqun already mentioned the "mixing access sizes", which is actually
> > > > > > quite fundamental in the kernel, where we play lots of games with that
> > > > > > (typically around locking, where you find patterns line unlock writing
> > > > > > a zero to a single byte, even though the whole lock data structure is
> > > > > > a word). And sometimes the access size games are very explicit (eg
> > > > > > lib/lockref.c).
> > > > >
> > > > > I don't think mixing access sizes should be a real barrier. On the read
> > > >
> > > > Well, it actually is, since mixing access sizes is, guess what,
> > > > an undefined behavior:
> > > >
> > > > (example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
> > > >
> > > > thread::scope(|s| {
> > > > // This is UB: using different-sized atomic accesses to the same data
> > > > s.spawn(|| atomic.store(1, Ordering::Relaxed));
> > > > s.spawn(|| unsafe {
> > > > let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
> > > > differently_sized.store(2, Ordering::Relaxed);
> > > > });
> > > > });
> > > >
> > > > Of course, you can say "I will just ignore the UB", but if you have to
> > > > ignore "compiler rules" to make your code work, why bother use compiler
> > > > builtin in the first place? Being UB means they are NOT guaranteed to
> > > > work.
> > >
> > > That's not what I'm proposing - you'd need additional compiler support.
> >
> > Ah, OK.
> >
> > > but the new intrinsic would be no different, semantics wise for the
> > > compiler to model, than a "lock orb".
> >
> > Be ready to be disappointed:
> >
> > https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402078545
> > https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402082631
> >
> > ;-)
> >
> > In fact, if you get a chance to read the previous discussion links I
> > shared, you will find I was just like you in the beginning: hope we
> > could extend the model to support more kernel code properly. But my
> > overall feeling is that it's either very challenging or lack of
> > motivation to do.
>
> That's casting - that doesn't work because compiler people hate
> aliasing.
>
> But intrinsics for e.g.
> __atomic32_read_u8(atomic_u32_t *a, unsigned byte)
> __atomic32_write_u8(atomic_u32_t a*, unsigned byte)
>
so "byte" here is the byte indexing in the u32? Hmm... I guess that'll
work. But I really don't know whether LLVM/Rust will support such an
intrinsic...
Regards,
Boqun
> should be doable - that's perfectly fine for the compiler to model.
>
> That would admittedly be ugly to use. But, if Rust ever allowed for
> marking any struct up to word size as atomic (which we want anyways...),
> it could use that under the hood for setting a member variable without
> cmpxchg.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [RFC PATCH v3 1/5] arm64: mm: swap: support THP_SWAP on hardware with MTE
From: Barry Song @ 2024-03-23 3:50 UTC (permalink / raw)
To: Chris Li
Cc: Ryan Roberts, akpm, linux-mm, chengming.zhou, david, hannes,
kasong, linux-arm-kernel, linux-kernel, mhocko, nphamcs,
shy828301, steven.price, surenb, wangkefeng.wang, willy, xiang,
ying.huang, yosryahmed, yuzhao, Barry Song, Catalin Marinas,
Will Deacon, Mark Rutland, Kemeng Shi, Anshuman Khandual,
Peter Collingbourne, Peter Xu, Lorenzo Stoakes,
Mike Rapoport (IBM), Hugh Dickins, Aneesh Kumar K.V,
Rick Edgecombe
In-Reply-To: <CAF8kJuNurcw5ufRZ=roNA01xpSNKDhq7dRF91sFQ_KfBNwNPcg@mail.gmail.com>
On Sat, Mar 23, 2024 at 3:15 PM Chris Li <chrisl@kernel.org> wrote:
>
> On Fri, Mar 22, 2024 at 3:19 AM Ryan Roberts <ryan.roberts@arm.com> wrote:
> >
> > On 22/03/2024 07:41, Barry Song wrote:
> > > On Fri, Mar 22, 2024 at 3:51 PM Barry Song <21cnbao@gmail.com> wrote:
> > >>
> > >> On Thu, Mar 21, 2024 at 11:31 PM Ryan Roberts <ryan.roberts@arm.com> wrote:
> > >>>
> > >>> On 21/03/2024 08:42, Barry Song wrote:
> > >>>> Hi Ryan,
> > >>>> Sorry for the late reply.
> > >>>
> > >>> No problem!
> > >>>
> > >>>>
> > >>>> On Tue, Mar 12, 2024 at 5:56 AM Ryan Roberts <ryan.roberts@arm.com> wrote:
> > >>>>>
> > >>>>> On 04/03/2024 08:13, Barry Song wrote:
> > >>>>>> From: Barry Song <v-songbaohua@oppo.com>
> > >>>>>>
> > >>>>>> Commit d0637c505f8a1 ("arm64: enable THP_SWAP for arm64") brings up
> > >>>>>> THP_SWAP on ARM64, but it doesn't enable THP_SWP on hardware with
> > >>>>>> MTE as the MTE code works with the assumption tags save/restore is
> > >>>>>> always handling a folio with only one page.
> > >>>>>>
> > >>>>>> The limitation should be removed as more and more ARM64 SoCs have
> > >>>>>> this feature. Co-existence of MTE and THP_SWAP becomes more and
> > >>>>>> more important.
> > >>>>>>
> > >>>>>> This patch makes MTE tags saving support large folios, then we don't
> > >>>>>> need to split large folios into base pages for swapping out on ARM64
> > >>>>>> SoCs with MTE any more.
> > >>>>>>
> > >>>>>> arch_prepare_to_swap() should take folio rather than page as parameter
> > >>>>>> because we support THP swap-out as a whole. It saves tags for all
> > >>>>>> pages in a large folio.
> > >>>>>>
> > >>>>>> As now we are restoring tags based-on folio, in arch_swap_restore(),
> > >>>>>> we may increase some extra loops and early-exitings while refaulting
> > >>>>>> a large folio which is still in swapcache in do_swap_page(). In case
> > >>>>>> a large folio has nr pages, do_swap_page() will only set the PTE of
> > >>>>>> the particular page which is causing the page fault.
> > >>>>>> Thus do_swap_page() runs nr times, and each time, arch_swap_restore()
> > >>>>>> will loop nr times for those subpages in the folio. So right now the
> > >>>>>> algorithmic complexity becomes O(nr^2).
> > >>>>>>
> > >>>>>> Once we support mapping large folios in do_swap_page(), extra loops
> > >>>>>> and early-exitings will decrease while not being completely removed
> > >>>>>> as a large folio might get partially tagged in corner cases such as,
> > >>>>>> 1. a large folio in swapcache can be partially unmapped, thus, MTE
> > >>>>>> tags for the unmapped pages will be invalidated;
> > >>>>>> 2. users might use mprotect() to set MTEs on a part of a large folio.
> > >>>>>>
> > >>>>>> arch_thp_swp_supported() is dropped since ARM64 MTE was the only one
> > >>>>>> who needed it.
> > >>>
> > >>> I think we should decouple this patch from your swap-in series. I suspect this
> > >>> one could be ready and go in sooner than the swap-in series based on the current
> > >>> discussions :)
> > >>>
> > >>>>>>
> > >>>>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
> > >>>>>> Cc: Will Deacon <will@kernel.org>
> > >>>>>> Cc: Ryan Roberts <ryan.roberts@arm.com>
> > >>>>>> Cc: Mark Rutland <mark.rutland@arm.com>
> > >>>>>> Cc: David Hildenbrand <david@redhat.com>
> > >>>>>> Cc: Kemeng Shi <shikemeng@huaweicloud.com>
> > >>>>>> Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
> > >>>>>> Cc: Anshuman Khandual <anshuman.khandual@arm.com>
> > >>>>>> Cc: Peter Collingbourne <pcc@google.com>
> > >>>>>> Cc: Steven Price <steven.price@arm.com>
> > >>>>>> Cc: Yosry Ahmed <yosryahmed@google.com>
> > >>>>>> Cc: Peter Xu <peterx@redhat.com>
> > >>>>>> Cc: Lorenzo Stoakes <lstoakes@gmail.com>
> > >>>>>> Cc: "Mike Rapoport (IBM)" <rppt@kernel.org>
> > >>>>>> Cc: Hugh Dickins <hughd@google.com>
> > >>>>>> CC: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
> > >>>>>> Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
> > >>>>>> Signed-off-by: Barry Song <v-songbaohua@oppo.com>
> > >>>>>> Reviewed-by: Steven Price <steven.price@arm.com>
> > >>>>>> Acked-by: Chris Li <chrisl@kernel.org>
> > >>>>>> ---
> > >>>>>> arch/arm64/include/asm/pgtable.h | 19 ++------------
> > >>>>>> arch/arm64/mm/mteswap.c | 43 ++++++++++++++++++++++++++++++++
> > >>>>>> include/linux/huge_mm.h | 12 ---------
> > >>>>>> include/linux/pgtable.h | 2 +-
> > >>>>>> mm/page_io.c | 2 +-
> > >>>>>> mm/swap_slots.c | 2 +-
> > >>>>>> 6 files changed, 48 insertions(+), 32 deletions(-)
> > >>>>>>
> > >>>>>> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> > >>>>>> index 401087e8a43d..7a54750770b8 100644
> > >>>>>> --- a/arch/arm64/include/asm/pgtable.h
> > >>>>>> +++ b/arch/arm64/include/asm/pgtable.h
> > >>>>>> @@ -45,12 +45,6 @@
> > >>>>>> __flush_tlb_range(vma, addr, end, PUD_SIZE, false, 1)
> > >>>>>> #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
> > >>>>>>
> > >>>>>> -static inline bool arch_thp_swp_supported(void)
> > >>>>>> -{
> > >>>>>> - return !system_supports_mte();
> > >>>>>> -}
> > >>>>>> -#define arch_thp_swp_supported arch_thp_swp_supported
> > >>>>>> -
> > >>>>>> /*
> > >>>>>> * Outside of a few very special situations (e.g. hibernation), we always
> > >>>>>> * use broadcast TLB invalidation instructions, therefore a spurious page
> > >>>>>> @@ -1095,12 +1089,7 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
> > >>>>>> #ifdef CONFIG_ARM64_MTE
> > >>>>>>
> > >>>>>> #define __HAVE_ARCH_PREPARE_TO_SWAP
> > >>>>>> -static inline int arch_prepare_to_swap(struct page *page)
> > >>>>>> -{
> > >>>>>> - if (system_supports_mte())
> > >>>>>> - return mte_save_tags(page);
> > >>>>>> - return 0;
> > >>>>>> -}
> > >>>>>> +extern int arch_prepare_to_swap(struct folio *folio);
> > >>>>>>
> > >>>>>> #define __HAVE_ARCH_SWAP_INVALIDATE
> > >>>>>> static inline void arch_swap_invalidate_page(int type, pgoff_t offset)
> > >>>>>> @@ -1116,11 +1105,7 @@ static inline void arch_swap_invalidate_area(int type)
> > >>>>>> }
> > >>>>>>
> > >>>>>> #define __HAVE_ARCH_SWAP_RESTORE
> > >>>>>> -static inline void arch_swap_restore(swp_entry_t entry, struct folio *folio)
> > >>>>>> -{
> > >>>>>> - if (system_supports_mte())
> > >>>>>> - mte_restore_tags(entry, &folio->page);
> > >>>>>> -}
> > >>>>>> +extern void arch_swap_restore(swp_entry_t entry, struct folio *folio);
> > >>>>>>
> > >>>>>> #endif /* CONFIG_ARM64_MTE */
> > >>>>>>
> > >>>>>> diff --git a/arch/arm64/mm/mteswap.c b/arch/arm64/mm/mteswap.c
> > >>>>>> index a31833e3ddc5..295836fef620 100644
> > >>>>>> --- a/arch/arm64/mm/mteswap.c
> > >>>>>> +++ b/arch/arm64/mm/mteswap.c
> > >>>>>> @@ -68,6 +68,13 @@ void mte_invalidate_tags(int type, pgoff_t offset)
> > >>>>>> mte_free_tag_storage(tags);
> > >>>>>> }
> > >>>>>>
> > >>>>>> +static inline void __mte_invalidate_tags(struct page *page)
> > >>>>>> +{
> > >>>>>> + swp_entry_t entry = page_swap_entry(page);
> > >>>>>> +
> > >>>>>> + mte_invalidate_tags(swp_type(entry), swp_offset(entry));
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> void mte_invalidate_tags_area(int type)
> > >>>>>> {
> > >>>>>> swp_entry_t entry = swp_entry(type, 0);
> > >>>>>> @@ -83,3 +90,39 @@ void mte_invalidate_tags_area(int type)
> > >>>>>> }
> > >>>>>> xa_unlock(&mte_pages);
> > >>>>>> }
> > >>>>>> +
> > >>>>>> +int arch_prepare_to_swap(struct folio *folio)
> > >>>>>> +{
> > >>>>>> + long i, nr;
> > >>>>>> + int err;
> > >>>>>> +
> > >>>>>> + if (!system_supports_mte())
> > >>>>>> + return 0;
> > >>>>>> +
> > >>>>>> + nr = folio_nr_pages(folio);
> > >>>>>> +
> > >>>>>> + for (i = 0; i < nr; i++) {
> > >>>>>> + err = mte_save_tags(folio_page(folio, i));
> > >>>>>> + if (err)
> > >>>>>> + goto out;
> > >>>>>> + }
> > >>>>>> + return 0;
> > >>>>>> +
> > >>>>>> +out:
> > >>>>>> + while (i--)
> > >>>>>> + __mte_invalidate_tags(folio_page(folio, i));
> > >>>>>> + return err;
> > >>>>>> +}
> > >>>>>> +
> > >>>>>> +void arch_swap_restore(swp_entry_t entry, struct folio *folio)
> > >>>>>
> > >>>>> I'm still not a fan of the fact that entry could be anywhere within folio.
> > >>>>>
> > >>>>>> +{
> > >>>>>> + if (system_supports_mte()) {
> > >>>>>
> > >>>>> nit: if you do:
> > >>>>>
> > >>>>> if (!system_supports_mte())
> > >>>>> return;
> > >>>>
> > >>>> Acked
> > >>>>
> > >>>>>
> > >>>>> It will be consistent with arch_prepare_to_swap() and reduce the indentation of
> > >>>>> the main body.
> > >>>>>
> > >>>>>> + long i, nr = folio_nr_pages(folio);
> > >>>>>> +
> > >>>>>> + entry.val -= swp_offset(entry) & (nr - 1);
> > >>>>>
> > >>>>> This assumes that folios are always stored in swap with natural alignment. Is
> > >>>>> that definitely a safe assumption? My swap-out series is currently ensuring that
> > >>>>> folios are swapped-out naturally aligned, but that is an implementation detail.
> > >>>>>
> > >>>>
> > >>>> I concur that this is an implementation detail. However, we should be
> > >>>> bold enough
> > >>>> to state that swap slots will be contiguous, considering we are
> > >>>> currently utilizing
> > >>>> folio->swap instead of subpage->swap ?
> > >>>
> > >>> Yes, I agree about contiguity. My objection is about assuming natural alignment
> > >>> though. It can still be contiguous while not naturally aligned in swap.
> > >>
> > >> Hi Ryan,
> > >>
> > >> While working on the new version of this patch, I've come to recognize
> > >> that, for the time being, it's
> > >> imperative to maintain a natural alignment. The following code
> > >> operates on the basis of this
> > >> assumption.
> > >>
> > >> /**
> > >> * folio_file_page - The page for a particular index.
> > >> * @folio: The folio which contains this index.
> > >> * @index: The index we want to look up.
> > >> *
> > >> * Sometimes after looking up a folio in the page cache, we need to
> > >> * obtain the specific page for an index (eg a page fault).
> > >> *
> > >> * Return: The page containing the file data for this index.
> > >> */
> > >> static inline struct page *folio_file_page(struct folio *folio, pgoff_t index)
> > >> {
> > >> return folio_page(folio, index & (folio_nr_pages(folio) - 1));
> > >> }
> > >>
> > >>
> > >> It's invoked everywhere, particularly within do_swap_page(). Nonetheless,
> > >> I remain confident that I can consistently pass the first entry to
> > >> arch_swap_restore().
> > >
> > > After grappling for a couple of hours, I've realized that the only
> > > viable approach
> > > is as follows: shifting the task of obtaining the first entry from the
> > > callee to the
> > > callers( looks silly). This is necessary due to various scenarios like
> > > swap cache,
> > > non-swap cache, and KSM, each presenting different cases. Since there's no
> > > assurance of folio->swap being present, forcibly setting folio->swap could pose
> > > risks (There might not even be any risk involved, but the associated
> > > task getting
> > > the first entry still cannot be overlooked by callers).
> > >
> > > diff --git a/mm/internal.h b/mm/internal.h
> > > index 7e486f2c502c..94d5b4b5a5da 100644
> > > --- a/mm/internal.h
> > > +++ b/mm/internal.h
> > > @@ -76,6 +76,20 @@ static inline int folio_nr_pages_mapped(struct folio *folio)
> > > return atomic_read(&folio->_nr_pages_mapped) & FOLIO_PAGES_MAPPED;
> > > }
> > >
> > > +/*
> > > + * Retrieve the first entry of a folio based on a provided entry within the
> > > + * folio. We cannot rely on folio->swap as there is no guarantee that it has
> > > + * been initialized. Used by arch_swap_restore()
> > > + */
> > > +static inline swp_entry_t folio_swap(swp_entry_t entry, struct folio *folio)
> > > +{
> > > + swp_entry_t swap = {
> > > + .val = entry.val & (folio_nr_pages(folio) - 1),
> > > + };
> > > +
> > > + return swap;
> > > +}
> > > +
> > > static inline void *folio_raw_mapping(struct folio *folio)
> > > {
> > > unsigned long mapping = (unsigned long)folio->mapping;
> > > diff --git a/mm/memory.c b/mm/memory.c
> > > index f2bc6dd15eb8..b7cab8be8632 100644
> > > --- a/mm/memory.c
> > > +++ b/mm/memory.c
> > > @@ -4188,7 +4188,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> > > * when reading from swap. This metadata may be indexed by swap entry
> > > * so this must be called before swap_free().
> > > */
> > > - arch_swap_restore(entry, folio);
> > > + arch_swap_restore(folio_swap(entry, folio), folio);
> > >
> > > /*
> > > * Remove the swap entry and conditionally try to free up the swapcache.
> > > diff --git a/mm/shmem.c b/mm/shmem.c
> > > index 0aad0d9a621b..82c9df4628f2 100644
> > > --- a/mm/shmem.c
> > > +++ b/mm/shmem.c
> > > @@ -1913,7 +1913,7 @@ static int shmem_swapin_folio(struct inode
> > > *inode, pgoff_t index,
> > > * Some architectures may have to restore extra metadata to the
> > > * folio after reading from swap.
> > > */
> > > - arch_swap_restore(swap, folio);
> > > + arch_swap_restore(folio_swap(entry, folio), folio);
> > >
> > > if (shmem_should_replace_folio(folio, gfp)) {
> > > error = shmem_replace_folio(&folio, gfp, info, index);
> > > diff --git a/mm/swapfile.c b/mm/swapfile.c
> > > index 4919423cce76..5e6d2304a2a4 100644
> > > --- a/mm/swapfile.c
> > > +++ b/mm/swapfile.c
> > > @@ -1806,7 +1806,7 @@ static int unuse_pte(struct vm_area_struct *vma,
> > > pmd_t *pmd,
> > > * when reading from swap. This metadata may be indexed by swap entry
> > > * so this must be called before swap_free().
> > > */
> > > - arch_swap_restore(entry, folio);
> > > + arch_swap_restore(folio_swap(entry, folio), folio);
> > >
> > > dec_mm_counter(vma->vm_mm, MM_SWAPENTS);
> > > inc_mm_counter(vma->vm_mm, MM_ANONPAGES);
> > >
> > >
> > > Meanwhile, natural alignment is essential even during the execution of
> > > add_to_swap(), as failure to
> > > do so will trigger the VM_BUG_ON condition below.
> > >
> > > int add_to_swap_cache(struct folio *folio, swp_entry_t entry,
> > > gfp_t gfp, void **shadowp)
> > > {
> > > struct address_space *address_space = swap_address_space(entry);
> > > pgoff_t idx = swp_offset(entry);
> > > XA_STATE_ORDER(xas, &address_space->i_pages, idx, folio_order(folio));
> > > unsigned long i, nr = folio_nr_pages(folio);
> > > ...
> > > folio_set_swapcache(folio);
> > > folio->swap = entry;
> > >
> > > do {
> > > xas_lock_irq(&xas);
> > > xas_create_range(&xas);
> > > if (xas_error(&xas))
> > > goto unlock;
> > > for (i = 0; i < nr; i++) {
> > > VM_BUG_ON_FOLIO(xas.xa_index != idx + i, folio);
>
> Here swap_cache assue swap entry + i match folio + i subpage. The swap
> entry of a folio must be continuous. If we want to allow folio write
even more than this. XA_STATE_ORDER ensures that
xas.xa_index is already naturally aligned by having
(index >> order) << order.
#define XA_STATE_ORDER(name, array, index, order) \
struct xa_state name = __XA_STATE(array, \
(index >> order) << order, \
order - (order % XA_CHUNK_SHIFT), \
(1U << (order % XA_CHUNK_SHIFT)) - 1)
> out to the discontiguous offset of the swap device, this aspect of the
> swap cache will need to change as well. Do you see a problem having
> all pte entries of a folio point to the same large swap entry? Of
> course, the large swap entry internally will track the offset of sub
> page + i. The swap cache will only have one index for the large swap
> entry (the head entry).
I do see two problems(or difficulties).
1. A specific page table entry (PTE) may not always be aware of its
position. For instance,
the second PTE of a large folio might not identify itself as such,
particularly if the virtual
address of the large folio is not aligned with the size of the large
folio due to operations
like mremap.
2. We also need to consider the complexity arising from partial
unmapping or DONTNEED
operations if we allow all PTEs to reference a 'large' swap entry.
Given that userspace
typically operates at 4KiB granularity, numerous partial unmappings
may be expected
for a single large swap entry.
>
>
> > > if (shadowp) {
> > > old = xas_load(&xas);
> > > if (xa_is_value(old))
> > > *shadowp = old;
> > > }
> > > xas_store(&xas, folio);
> > > xas_next(&xas);
> > > }
> > > }
> > >
> > >
> > > Based on the information provided, Ryan, would it be feasible to retain the task
> > > of obtaining the first entry within the callee? Or, are you in favor
> > > of utilizing the
> > > new folio_swap() helper?
> >
> > My opinion still remains that either:
> >
> > - This should be a per-page interface - i.e. call it for each page to restore
> > tags. If we don't want to pass `struct page *` then perhaps we can pass a folio
>
> Can you clarify that by "tag" you mean the MTE tags, not swap cache
> xarray tags, right? From the email context I assume that is the MTE
> tag. Please let me know if I assume incorrectly.
>
> > and the index of the page we want to restore? In this case, entry refers the the
> > precise page we are operating on.
> >
> > OR
> >
> > - Make it a per-folio interface - i.e. it restores tags for all pages in the
> > folio. But in this case, entry must refer to the first page in the folio.
> > Anything else is confusing.
>
> As long as you refer to the subpage as folilo + i, restoring a subset
> of the folio should be permitted?
That was my approach in those older versions - passing subpage
rather than folio.
In recent versions, we've transitioned to always restoring a folio during
swap operations. While this approach is acceptable, it can lead to some
redundant idle loops. For instance, if we swap out a large folio with
nr_pages and subsequently encounter a refault while the folio is still
in the swapcache, the first do_swap_page() call will restore all tags.
Subsequent do_swap_page() calls for the remaining nr_pages-1 PTEs
will perform the same checks, realizing that the restoration has already
been completed and thus skipping the process. However, they still
redundantly execute checks.
I propose extracting a separate patch from "[RFC PATCH v3 5/5] mm:
support large folios swapin as a whole" specifically to handle refaults.
This patch would essentially remove these redundant loops. The
swap-in patch currently addresses both refaults and newly allocated
large folios. If we prioritize addressing refaults sooner, I believe
this extraction would be beneficial.
>
> On the swap entry side, I would like to avoid assuming the swap entry
> is contingues. The swap entry should have an API to fetch the swap
> offset of the head entry + i. For the simple continuous swap entry,
> this mapping is just linear. For non continuous swap offset, it would
> need to go through some lookup table to find the offset for i.
The implementation of this approach necessitates a significant overhaul
of existing infrastructure. Currently, the entire codebase operates under
the assumption of contiguous and naturally aligned swap entries. As such,
adapting the system to support non-contiguous or non-naturally aligned
swap entries will require substantial modifications across various
components.
>
> Chris
>
> >
> > So if going for the latter approach, then I vote for fixing it up in the callee.
> > But I'm just one guy with one opinion!
> >
> >
> > >
> > >>
> > >>>
> > >>>>
> > >>>>> Your cover note for swap-in says that you could technically swap in a large
> > >>>>> folio without it having been swapped-out large. If you chose to do that in
> > >>>>> future, this would break, right? I don't think it's good to couple the swap
> > >>>>
> > >>>> Right. technically I agree. Given that we still have many tasks involving even
> > >>>> swapping in contiguous swap slots, it's unlikely that swapping in large folios
> > >>>> for non-contiguous entries will occur in the foreseeable future :-)
> > >>>>
> > >>>>> storage layout to the folio order that you want to swap into. Perhaps that's an
> > >>>>> argument for passing each *page* to this function with its exact, corresponding
> > >>>>> swap entry?
> > >>>>
> > >>>> I recall Matthew Wilcox strongly objected to using "page" as the
> > >>>> parameter, so I've
> > >>>> discarded that approach. Alternatively, it appears I can consistently pass
> > >>>> folio->swap to this function and ensure the function always retrieves
> > >>>> the first entry?
> > >>>
> > >>> Yes, if we must pass a folio here, I'd prefer that entry always corresponds to
> > >>> the first entry for the folio. That will remove the need for this function to do
> > >>> the alignment above too. So win-win.
> > >>>
> > >>>>
> > >>>>>
> > >>>>>> + for (i = 0; i < nr; i++) {
> > >>>>>> + mte_restore_tags(entry, folio_page(folio, i));
> > >>>>>> + entry.val++;
> > >>>>>> + }
> > >>>>>> + }
> > >>>>>> +}
> > >>>>>> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > >>>>>> index de0c89105076..e04b93c43965 100644
> > >>>>>> --- a/include/linux/huge_mm.h
> > >>>>>> +++ b/include/linux/huge_mm.h
> > >>>>>> @@ -535,16 +535,4 @@ static inline int split_folio_to_order(struct folio *folio, int new_order)
> > >>>>>> #define split_folio_to_list(f, l) split_folio_to_list_to_order(f, l, 0)
> > >>>>>> #define split_folio(f) split_folio_to_order(f, 0)
> > >>>>>>
> > >>>>>> -/*
> > >>>>>> - * archs that select ARCH_WANTS_THP_SWAP but don't support THP_SWP due to
> > >>>>>> - * limitations in the implementation like arm64 MTE can override this to
> > >>>>>> - * false
> > >>>>>> - */
> > >>>>>> -#ifndef arch_thp_swp_supported
> > >>>>>> -static inline bool arch_thp_swp_supported(void)
> > >>>>>> -{
> > >>>>>> - return true;
> > >>>>>> -}
> > >>>>>> -#endif
> > >>>>>> -
> > >>>>>> #endif /* _LINUX_HUGE_MM_H */
> > >>>>>> diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
> > >>>>>> index e1b22903f709..bfcfe3386934 100644
> > >>>>>> --- a/include/linux/pgtable.h
> > >>>>>> +++ b/include/linux/pgtable.h
> > >>>>>> @@ -1106,7 +1106,7 @@ static inline int arch_unmap_one(struct mm_struct *mm,
> > >>>>>> * prototypes must be defined in the arch-specific asm/pgtable.h file.
> > >>>>>> */
> > >>>>>> #ifndef __HAVE_ARCH_PREPARE_TO_SWAP
> > >>>>>> -static inline int arch_prepare_to_swap(struct page *page)
> > >>>>>> +static inline int arch_prepare_to_swap(struct folio *folio)
> > >>>>>> {
> > >>>>>> return 0;
> > >>>>>> }
> > >>>>>> diff --git a/mm/page_io.c b/mm/page_io.c
> > >>>>>> index ae2b49055e43..a9a7c236aecc 100644
> > >>>>>> --- a/mm/page_io.c
> > >>>>>> +++ b/mm/page_io.c
> > >>>>>> @@ -189,7 +189,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
> > >>>>>> * Arch code may have to preserve more data than just the page
> > >>>>>> * contents, e.g. memory tags.
> > >>>>>> */
> > >>>>>> - ret = arch_prepare_to_swap(&folio->page);
> > >>>>>> + ret = arch_prepare_to_swap(folio);
> > >>>>>> if (ret) {
> > >>>>>> folio_mark_dirty(folio);
> > >>>>>> folio_unlock(folio);
> > >>>>>> diff --git a/mm/swap_slots.c b/mm/swap_slots.c
> > >>>>>> index 90973ce7881d..53abeaf1371d 100644
> > >>>>>> --- a/mm/swap_slots.c
> > >>>>>> +++ b/mm/swap_slots.c
> > >>>>>> @@ -310,7 +310,7 @@ swp_entry_t folio_alloc_swap(struct folio *folio)
> > >>>>>> entry.val = 0;
> > >>>>>>
> > >>>>>> if (folio_test_large(folio)) {
> > >>>>>> - if (IS_ENABLED(CONFIG_THP_SWAP) && arch_thp_swp_supported())
> > >>>>>> + if (IS_ENABLED(CONFIG_THP_SWAP))
> > >>>>>> get_swap_pages(1, &entry, folio_nr_pages(folio));
> > >>>>>> goto out;
> > >>>>>> }
> > >>>>>
> > >>>>
> > >>>> Thanks
> > >>>> Barry
Thanks
Barry
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/2] ASoC: dt-bindings: fsl-asoc-card: convert to YAML
From: Shengjiu Wang @ 2024-03-23 3:42 UTC (permalink / raw)
To: Rob Herring
Cc: Shengjiu Wang, lgirdwood, broonie, krzysztof.kozlowski+dt,
conor+dt, linux-sound, devicetree, linux-kernel, shawnguo,
s.hauer, kernel, festevam, imx, linux-arm-kernel
In-Reply-To: <20240322135800.GA811908-robh@kernel.org>
On Fri, Mar 22, 2024 at 9:58 PM Rob Herring <robh@kernel.org> wrote:
>
> On Fri, Mar 22, 2024 at 06:13:25PM +0800, Shengjiu Wang wrote:
> > Convert the fsl-asoc-card binding to YAML.
> >
> > In order to pass the checking, add some used compatible
> > string from devicetree.
> >
> > Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> > ---
> > .../bindings/sound/fsl-asoc-card.txt | 117 -----------
> > .../bindings/sound/fsl-asoc-card.yaml | 196 ++++++++++++++++++
> > 2 files changed, 196 insertions(+), 117 deletions(-)
> > delete mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
> > create mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
> > deleted file mode 100644
> > index 4e8dbc5abfd1..000000000000
> > --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
> > +++ /dev/null
> > @@ -1,117 +0,0 @@
> > -Freescale Generic ASoC Sound Card with ASRC support
> > -
> > -The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
> > -SoCs connecting with external CODECs.
> > -
> > -The idea of this generic sound card is a bit like ASoC Simple Card. However,
> > -for Freescale SoCs (especially those released in recent years), most of them
> > -have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
> > -this is a specific feature that might be painstakingly controlled and merged
> > -into the Simple Card.
> > -
> > -So having this generic sound card allows all Freescale SoC users to benefit
> > -from the simplification of a new card support and the capability of the wide
> > -sample rates support through ASRC.
> > -
> > -Note: The card is initially designed for those sound cards who use AC'97, I2S
> > - and PCM DAI formats. However, it'll be also possible to support those non
> > - AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
> > - long as the driver has been properly upgraded.
> > -
> > -
> > -The compatible list for this generic sound card currently:
> > - "fsl,imx-audio-ac97"
> > -
> > - "fsl,imx-audio-cs42888"
> > -
> > - "fsl,imx-audio-cs427x"
> > - (compatible with CS4271 and CS4272)
> > -
> > - "fsl,imx-audio-wm8962"
> > -
> > - "fsl,imx-audio-sgtl5000"
> > - (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
> > -
> > - "fsl,imx-audio-wm8960"
> > -
> > - "fsl,imx-audio-mqs"
> > -
> > - "fsl,imx-audio-wm8524"
> > -
> > - "fsl,imx-audio-tlv320aic32x4"
> > -
> > - "fsl,imx-audio-tlv320aic31xx"
> > -
> > - "fsl,imx-audio-si476x"
> > -
> > - "fsl,imx-audio-wm8958"
> > -
> > - "fsl,imx-audio-nau8822"
> > -
> > -Required properties:
> > -
> > - - compatible : Contains one of entries in the compatible list.
> > -
> > - - model : The user-visible name of this sound complex
> > -
> > - - audio-cpu : The phandle of an CPU DAI controller
> > -
> > - - audio-codec : The phandle of an audio codec
> > -
> > -Optional properties:
> > -
> > - - audio-asrc : The phandle of ASRC. It can be absent if there's no
> > - need to add ASRC support via DPCM.
> > -
> > - - audio-routing : A list of the connections between audio components.
> > - Each entry is a pair of strings, the first being the
> > - connection's sink, the second being the connection's
> > - source. There're a few pre-designed board connectors:
> > - * Line Out Jack
> > - * Line In Jack
> > - * Headphone Jack
> > - * Mic Jack
> > - * Ext Spk
> > - * AMIC (stands for Analog Microphone Jack)
> > - * DMIC (stands for Digital Microphone Jack)
> > -
> > - Note: The "Mic Jack" and "AMIC" are redundant while
> > - coexisting in order to support the old bindings
> > - of wm8962 and sgtl5000.
> > -
> > - - hp-det-gpio : The GPIO that detect headphones are plugged in
> > - - mic-det-gpio : The GPIO that detect microphones are plugged in
> > - - bitclock-master : Indicates dai-link bit clock master; for details see simple-card.yaml.
> > - - frame-master : Indicates dai-link frame master; for details see simple-card.yaml.
> > - - dai-format : audio format, for details see simple-card.yaml.
> > - - frame-inversion : dai-link uses frame clock inversion, for details see simple-card.yaml.
> > - - bitclock-inversion : dai-link uses bit clock inversion, for details see simple-card.yaml.
> > - - mclk-id : main clock id, specific for each card configuration.
> > -
> > -Optional unless SSI is selected as a CPU DAI:
> > -
> > - - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
> > -
> > - - mux-ext-port : The external port of the i.MX audio muxer
> > -
> > -Example:
> > -sound-cs42888 {
> > - compatible = "fsl,imx-audio-cs42888";
> > - model = "cs42888-audio";
> > - audio-cpu = <&esai>;
> > - audio-asrc = <&asrc>;
> > - audio-codec = <&cs42888>;
> > - audio-routing =
> > - "Line Out Jack", "AOUT1L",
> > - "Line Out Jack", "AOUT1R",
> > - "Line Out Jack", "AOUT2L",
> > - "Line Out Jack", "AOUT2R",
> > - "Line Out Jack", "AOUT3L",
> > - "Line Out Jack", "AOUT3R",
> > - "Line Out Jack", "AOUT4L",
> > - "Line Out Jack", "AOUT4R",
> > - "AIN1L", "Line In Jack",
> > - "AIN1R", "Line In Jack",
> > - "AIN2L", "Line In Jack",
> > - "AIN2R", "Line In Jack";
> > -};
> > diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
> > new file mode 100644
> > index 000000000000..48051655230d
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
> > @@ -0,0 +1,196 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/sound/fsl-asoc-card.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Freescale Generic ASoC Sound Card with ASRC support
> > +
> > +description:
> > + The Freescale Generic ASoC Sound Card can be used, ideally,
> > + for all Freescale SoCs connecting with external CODECs.
> > +
> > + The idea of this generic sound card is a bit like ASoC Simple Card.
> > + However, for Freescale SoCs (especially those released in recent years),
> > + most of them have ASRC
> > + (Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml) inside. And
> > + this is a specific feature that might be painstakingly controlled and
> > + merged into the Simple Card.
> > +
> > + So having this generic sound card allows all Freescale SoC users to
> > + benefit from the simplification of a new card support and the capability
> > + of the wide sample rates support through ASRC.
> > +
> > + Note, The card is initially designed for those sound cards who use AC'97, I2S
> > + and PCM DAI formats. However, it'll be also possible to support those non
> > + AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
> > + long as the driver has been properly upgraded.
> > +
> > +maintainers:
> > + - Shengjiu Wang <shengjiu.wang@nxp.com>
> > +
> > +properties:
> > + compatible:
> > + oneOf:
> > + - items:
> > + - enum:
> > + - fsl,imx-sgtl5000
> > + - fsl,imx53-cpuvo-sgtl5000
> > + - fsl,imx51-babbage-sgtl5000
> > + - fsl,imx53-m53evk-sgtl5000
> > + - fsl,imx53-qsb-sgtl5000
> > + - fsl,imx53-voipac-sgtl5000
> > + - fsl,imx6-armadeus-sgtl5000
> > + - fsl,imx6-rex-sgtl5000
> > + - fsl,imx6-sabreauto-cs42888
> > + - fsl,imx6-wandboard-sgtl5000
> > + - fsl,imx6dl-nit6xlite-sgtl5000
> > + - fsl,imx6q-ba16-sgtl5000
> > + - fsl,imx6q-nitrogen6_max-sgtl5000
> > + - fsl,imx6q-nitrogen6_som2-sgtl5000
> > + - fsl,imx6q-nitrogen6x-sgtl5000
> > + - fsl,imx6q-sabrelite-sgtl5000
> > + - fsl,imx6q-sabresd-wm8962
> > + - fsl,imx6q-udoo-ac97
> > + - fsl,imx6q-ventana-sgtl5000
> > + - fsl,imx6sl-evk-wm8962
> > + - fsl,imx6sx-sdb-mqs
> > + - fsl,imx6sx-sdb-wm8962
> > + - fsl,imx7d-evk-wm8960
> > + - karo,tx53-audio-sgtl5000
> > + - tq,imx53-mba53-sgtl5000
>
> None of these were documented before. It's fine to add all these in this
> patch, but please state in the commit message what missing or incorrect
> things you added in the schema.
ok, will add info in the commit message.
>
> > + - enum:
> > + - fsl,imx-audio-ac97
> > + - fsl,imx-audio-cs42888
> > + - fsl,imx-audio-mqs
> > + - fsl,imx-audio-sgtl5000
> > + - fsl,imx-audio-wm8960
> > + - fsl,imx-audio-wm8962
> > + - items:
> > + - enum:
> > + - fsl,imx-audio-ac97
> > + - fsl,imx-audio-cs42888
> > + - fsl,imx-audio-cs427x
> > + - fsl,imx-audio-mqs
> > + - fsl,imx-audio-nau8822
> > + - fsl,imx-audio-sgtl5000
> > + - fsl,imx-audio-si476x
> > + - fsl,imx-audio-tlv320aic31xx
> > + - fsl,imx-audio-tlv320aic32x4
> > + - fsl,imx-audio-wm8524
> > + - fsl,imx-audio-wm8960
> > + - fsl,imx-audio-wm8962
> > + - fsl,imx-audio-wm8958
> > +
> > + model:
> > + $ref: /schemas/types.yaml#/definitions/string
> > + description: The user-visible name of this sound complex
> > +
> > + audio-asrc:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description:
> > + The phandle of ASRC. It can be absent if there's no
> > + need to add ASRC support via DPCM.
> > +
> > + audio-codec:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description: The phandle of an audio codec
> > +
> > + audio-cpu:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description: The phandle of an CPU DAI controller
> > +
> > + audio-routing:
> > + $ref: /schemas/types.yaml#/definitions/non-unique-string-array
> > + description:
> > + A list of the connections between audio components. Each entry is a
> > + pair of strings, the first being the connection's sink, the second
> > + being the connection's source. There're a few pre-designed board
> > + connectors.
> > + * Line Out Jack
> > + * Line In Jack
> > + * Headphone Jack
> > + * Mic Jack
> > + * Ext Spk
> > + * AMIC (stands for Analog Microphone Jack)
> > + * DMIC (stands for Digital Microphone Jack)
> > + Note, The "Mic Jack" and "AMIC" are redundant while coexisting in
> > + order to support the old bindings of wm8962 and sgtl5000.
>
> Please list the strings out as schema:
>
> minItems: 2
> items:
> enum:
> - "Line Out Jack"
> ...
Can I not list them? Because this fsl-asoc-card is generic
it supports several codecs, if list them, there will be a long list.
>
> > +
> > + hp-det-gpio:
> > + maxItems: 1
> > + description: The GPIO that detect headphones are plugged in
>
> deprecated: true
ok, will add it.
>
> > +
> > + hp-det-gpios:
> > + maxItems: 1
> > + description: The GPIO that detect headphones are plugged in
> > +
> > + mic-det-gpio:
> > + maxItems: 1
> > + description: The GPIO that detect microphones are plugged in
>
> deprecated: true
>
> > +
> > + mic-det-gpios:
> > + maxItems: 1
> > + description: The GPIO that detect microphones are plugged in
> > +
> > + bitclock-master:
> > + description: Indicates dai-link bit clock master; for details see simple-card.yaml.
>
> Drop the prose reference and add:
>
> $ref: simple-card.yaml#/definitions/bitclock-master
>
> Otherwise, bitclock-master could be anything.
>
> And similar for the rest.
ok, will change them.
Best regards
Shengjiu Wang
>
> > +
> > + frame-master:
> > + description: Indicates dai-link frame master; for details see simple-card.yaml.
> > +
> > + dai-format:
> > + description: audio format, for details see simple-card.yaml.
> > +
> > + frame-inversion:
> > + description: dai-link uses frame clock inversion, for details see simple-card.yaml.
> > +
> > + bitclock-inversion:
> > + description: dai-link uses bit clock inversion, for details see simple-card.yaml.
> > +
> > + mclk-id:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: main clock id, specific for each card configuration.
> > +
> > + mux-int-port:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + enum: [1, 2, 7]
> > + description: The internal port of the i.MX audio muxer (AUDMUX)
> > +
> > + mux-ext-port:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + enum: [3, 4, 5, 6]
> > + description: The external port of the i.MX audio muxer
> > +
> > + ssi-controller:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description: The phandle of an CPU DAI controller
> > +
> > +required:
> > + - compatible
> > + - model
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > + - |
> > + sound-cs42888 {
> > + compatible = "fsl,imx-audio-cs42888";
> > + model = "cs42888-audio";
> > + audio-cpu = <&esai>;
> > + audio-asrc = <&asrc>;
> > + audio-codec = <&cs42888>;
> > + audio-routing =
> > + "Line Out Jack", "AOUT1L",
> > + "Line Out Jack", "AOUT1R",
> > + "Line Out Jack", "AOUT2L",
> > + "Line Out Jack", "AOUT2R",
> > + "Line Out Jack", "AOUT3L",
> > + "Line Out Jack", "AOUT3R",
> > + "Line Out Jack", "AOUT4L",
> > + "Line Out Jack", "AOUT4R",
> > + "AIN1L", "Line In Jack",
> > + "AIN1R", "Line In Jack",
> > + "AIN2L", "Line In Jack",
> > + "AIN2R", "Line In Jack";
> > + };
> > --
> > 2.34.1
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Kent Overstreet @ 2024-03-23 3:10 UTC (permalink / raw)
To: Boqun Feng
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <Zf5FEFCfuy0TAjV6@Boquns-Mac-mini.home>
On Fri, Mar 22, 2024 at 07:57:20PM -0700, Boqun Feng wrote:
> On Fri, Mar 22, 2024 at 10:33:13PM -0400, Kent Overstreet wrote:
> > On Fri, Mar 22, 2024 at 07:26:28PM -0700, Boqun Feng wrote:
> > > On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
> > > [...]
> > > > > Boqun already mentioned the "mixing access sizes", which is actually
> > > > > quite fundamental in the kernel, where we play lots of games with that
> > > > > (typically around locking, where you find patterns line unlock writing
> > > > > a zero to a single byte, even though the whole lock data structure is
> > > > > a word). And sometimes the access size games are very explicit (eg
> > > > > lib/lockref.c).
> > > >
> > > > I don't think mixing access sizes should be a real barrier. On the read
> > >
> > > Well, it actually is, since mixing access sizes is, guess what,
> > > an undefined behavior:
> > >
> > > (example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
> > >
> > > thread::scope(|s| {
> > > // This is UB: using different-sized atomic accesses to the same data
> > > s.spawn(|| atomic.store(1, Ordering::Relaxed));
> > > s.spawn(|| unsafe {
> > > let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
> > > differently_sized.store(2, Ordering::Relaxed);
> > > });
> > > });
> > >
> > > Of course, you can say "I will just ignore the UB", but if you have to
> > > ignore "compiler rules" to make your code work, why bother use compiler
> > > builtin in the first place? Being UB means they are NOT guaranteed to
> > > work.
> >
> > That's not what I'm proposing - you'd need additional compiler support.
>
> Ah, OK.
>
> > but the new intrinsic would be no different, semantics wise for the
> > compiler to model, than a "lock orb".
>
> Be ready to be disappointed:
>
> https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402078545
> https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402082631
>
> ;-)
>
> In fact, if you get a chance to read the previous discussion links I
> shared, you will find I was just like you in the beginning: hope we
> could extend the model to support more kernel code properly. But my
> overall feeling is that it's either very challenging or lack of
> motivation to do.
That's casting - that doesn't work because compiler people hate
aliasing.
But intrinsics for e.g.
__atomic32_read_u8(atomic_u32_t *a, unsigned byte)
__atomic32_write_u8(atomic_u32_t a*, unsigned byte)
should be doable - that's perfectly fine for the compiler to model.
That would admittedly be ugly to use. But, if Rust ever allowed for
marking any struct up to word size as atomic (which we want anyways...),
it could use that under the hood for setting a member variable without
cmpxchg.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v1 2/2] media: i2c: Add GC05A2 image sensor driver
From: Zhi Mao (毛智) @ 2024-03-23 2:59 UTC (permalink / raw)
To: sakari.ailus@linux.intel.com
Cc: heiko@sntech.de, gerald.loacker@wolfvision.net,
robh+dt@kernel.org, yunkec@chromium.org,
linux-kernel@vger.kernel.org, dan.scally@ideasonboard.com,
linux-media@vger.kernel.org,
Shengnan Wang (王圣男), hdegoede@redhat.com,
linus.walleij@linaro.org, andy.shevchenko@gmail.com,
Yaya Chang (張雅清), mchehab@kernel.org,
jacopo.mondi@ideasonboard.com, jernej.skrabec@gmail.com,
linux-mediatek@lists.infradead.org, bingbu.cao@intel.com,
Project_Global_Chrome_Upstream_Group, conor+dt@kernel.org,
10572168@qq.com, hverkuil-cisco@xs4all.nl,
tomi.valkeinen@ideasonboard.com,
krzysztof.kozlowski+dt@linaro.org,
linux-arm-kernel@lists.infradead.org, matthias.bgg@gmail.com,
laurent.pinchart@ideasonboard.com, devicetree@vger.kernel.org,
angelogioacchino.delregno@collabora.com, macromorgan@hotmail.com
In-Reply-To: <ZfqQPTgqzOw7tATK@kekkonen.localdomain>
Hi Sakari,
Thanks for your review.
On Wed, 2024-03-20 at 07:29 +0000, Sakari Ailus wrote:
>
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
> Hi Zhi,
>
> Thanks for the set.
>
> On Sat, Mar 16, 2024 at 10:52:53AM +0800, Zhi Mao wrote:
> > +static int gc05a2_set_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +struct gc05a2 *gc05a2 =
> > +container_of(ctrl->handler, struct gc05a2, ctrls);
> > +int ret = 0;
> > +s64 exposure_max;
> > +struct v4l2_subdev_state *state;
> > +const struct v4l2_mbus_framefmt *format;
> > +
> > +state = v4l2_subdev_get_locked_active_state(&gc05a2->sd);
> > +format = v4l2_subdev_state_get_format(state, 0);
> > +
> > +if (ctrl->id == V4L2_CID_VBLANK) {
> > +/* Update max exposure while meeting expected vblanking */
> > +exposure_max = format->height + ctrl->val - GC05A2_EXP_MARGIN;
> > +__v4l2_ctrl_modify_range(gc05a2->exposure,
> > + gc05a2->exposure->minimum,
> > + exposure_max, gc05a2->exposure->step,
> > + exposure_max);
> > +}
> > +
> > +/*
> > + * Applying V4L2 control value only happens
> > + * when power is on for streaming.
> > + */
> > +if (!pm_runtime_get_if_in_use(gc05a2->dev))
>
> This should be pm_runtime_get_if_active(). Please assume it takes a
> single
> argument (the device)---see commit
> c0ef3df8dbaef51ee4cfd58a471adf2eaee6f6b3.
>
> The same comment applies to the GC08A3 if it uses autosuspend, please
> post
> a new patch for that.
>
fixed, patch as below:
gc05a patch:v2
https://lore.kernel.org/linux-media/20240323014751.4989-1-zhi.mao@mediatek.com/
gc08a patch:v8
https://lore.kernel.org/linux-media/20240323023851.5503-1-zhi.mao@mediatek.com/
> --
> Kind regards,
>
> Sakari Ailus
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Boqun Feng @ 2024-03-23 2:57 UTC (permalink / raw)
To: Kent Overstreet
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <34r4signulvsclmsiqgghskmj5xce3zs5hwgfulzaez2wdyklr@ck6zrj732c4m>
On Fri, Mar 22, 2024 at 10:33:13PM -0400, Kent Overstreet wrote:
> On Fri, Mar 22, 2024 at 07:26:28PM -0700, Boqun Feng wrote:
> > On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
> > [...]
> > > > Boqun already mentioned the "mixing access sizes", which is actually
> > > > quite fundamental in the kernel, where we play lots of games with that
> > > > (typically around locking, where you find patterns line unlock writing
> > > > a zero to a single byte, even though the whole lock data structure is
> > > > a word). And sometimes the access size games are very explicit (eg
> > > > lib/lockref.c).
> > >
> > > I don't think mixing access sizes should be a real barrier. On the read
> >
> > Well, it actually is, since mixing access sizes is, guess what,
> > an undefined behavior:
> >
> > (example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
> >
> > thread::scope(|s| {
> > // This is UB: using different-sized atomic accesses to the same data
> > s.spawn(|| atomic.store(1, Ordering::Relaxed));
> > s.spawn(|| unsafe {
> > let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
> > differently_sized.store(2, Ordering::Relaxed);
> > });
> > });
> >
> > Of course, you can say "I will just ignore the UB", but if you have to
> > ignore "compiler rules" to make your code work, why bother use compiler
> > builtin in the first place? Being UB means they are NOT guaranteed to
> > work.
>
> That's not what I'm proposing - you'd need additional compiler support.
Ah, OK.
> but the new intrinsic would be no different, semantics wise for the
> compiler to model, than a "lock orb".
Be ready to be disappointed:
https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402078545
https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20atomic.20aliasing.20allowed.3F/near/402082631
;-)
In fact, if you get a chance to read the previous discussion links I
shared, you will find I was just like you in the beginning: hope we
could extend the model to support more kernel code properly. But my
overall feeling is that it's either very challenging or lack of
motivation to do.
Regards,
Boqun
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v8 2/2] media: i2c: Add GC08A3 image sensor driver
From: Zhi Mao @ 2024-03-23 2:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20240323023851.5503-1-zhi.mao@mediatek.com>
Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor.
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc08a3.c | 1339 ++++++++++++++++++++++++++++++++++++
3 files changed, 1350 insertions(+)
create mode 100644 drivers/media/i2c/gc08a3.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 56f276b920ab..e4da68835683 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -70,6 +70,16 @@ config VIDEO_GC0308
To compile this driver as a module, choose M here: the
module will be called gc0308.
+config VIDEO_GC08A3
+ tristate "GalaxyCore gc08a3 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gc08a3.
+
config VIDEO_GC2145
select V4L2_CCI_I2C
tristate "GalaxyCore GC2145 sensor support"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index dfbe6448b549..b82e99ca7578 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
obj-$(CONFIG_VIDEO_GC0308) += gc0308.o
+obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o
obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c
new file mode 100644
index 000000000000..7680d807e7a5
--- /dev/null
+++ b/drivers/media/i2c/gc08a3.c
@@ -0,0 +1,1339 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for GalaxyCore gc08a3 image sensor
+ *
+ * Copyright 2024 MediaTek
+ *
+ * Zhi Mao <zhi.mao@mediatek.com>
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define GC08A3_REG_TEST_PATTERN_EN CCI_REG8(0x008c)
+#define GC08A3_REG_TEST_PATTERN_IDX CCI_REG8(0x008d)
+#define GC08A3_TEST_PATTERN_EN 0x01
+
+#define GC08A3_STREAMING_REG CCI_REG8(0x0100)
+
+#define GC08A3_FLIP_REG CCI_REG8(0x0101)
+#define GC08A3_FLIP_H_MASK BIT(0)
+#define GC08A3_FLIP_V_MASK BIT(1)
+
+#define GC08A3_EXP_REG CCI_REG16(0x0202)
+#define GC08A3_EXP_MARGIN 16
+#define GC08A3_EXP_MIN 4
+#define GC08A3_EXP_STEP 1
+
+#define GC08A3_AGAIN_REG CCI_REG16(0x0204)
+#define GC08A3_AGAIN_MIN 1024
+#define GC08A3_AGAIN_MAX (1024 * 16)
+#define GC08A3_AGAIN_STEP 1
+
+#define GC08A3_FRAME_LENGTH_REG CCI_REG16(0x0340)
+#define GC08A3_VTS_MAX 0xfff0
+
+#define GC08A3_REG_CHIP_ID CCI_REG16(0x03f0)
+#define GC08A3_CHIP_ID 0x08a3
+
+#define GC08A3_NATIVE_WIDTH 3264
+#define GC08A3_NATIVE_HEIGHT 2448
+
+#define GC08A3_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ)
+#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10
+#define GC08A3_DATA_LANES 4
+
+#define GC08A3_RGB_DEPTH 10
+
+#define GC08A3_SLEEP_US (2 * USEC_PER_MSEC)
+
+static const char *const gc08a3_test_pattern_menu[] = {
+ "No Pattern", "Solid Black", "Colour Bar", "Solid White",
+ "Solid Red", "Solid Green", "Solid Blue", "Solid Yellow",
+};
+
+static const s64 gc08a3_link_freq_menu_items[] = {
+ (336 * HZ_PER_MHZ),
+ (207 * HZ_PER_MHZ),
+};
+
+static const char *const gc08a3_supply_name[] = {
+ "avdd",
+ "dvdd",
+ "dovdd",
+};
+
+struct gc08a3 {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct clk *xclk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(gc08a3_supply_name)];
+ struct gpio_desc *reset_gpio;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ struct regmap *regmap;
+ unsigned long link_freq_bitmap;
+ const struct gc08a3_mode *cur_mode;
+};
+
+struct gc08a3_reg_list {
+ u32 num_of_regs;
+ const struct cci_reg_sequence *regs;
+};
+
+static const struct cci_reg_sequence mode_3264x2448[] = {
+ /* system */
+ { CCI_REG8(0x0336), 0x70 },
+ { CCI_REG8(0x0383), 0xbb },
+ { CCI_REG8(0x0344), 0x00 },
+ { CCI_REG8(0x0345), 0x06 },
+ { CCI_REG8(0x0346), 0x00 },
+ { CCI_REG8(0x0347), 0x04 },
+ { CCI_REG8(0x0348), 0x0c },
+ { CCI_REG8(0x0349), 0xd0 },
+ { CCI_REG8(0x034a), 0x09 },
+ { CCI_REG8(0x034b), 0x9c },
+ { CCI_REG8(0x0202), 0x09 },
+ { CCI_REG8(0x0203), 0x04 },
+ { CCI_REG8(0x0340), 0x09 },
+ { CCI_REG8(0x0341), 0xf4 },
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x1c },
+
+ { CCI_REG8(0x0226), 0x00 },
+ { CCI_REG8(0x0227), 0x28 },
+ { CCI_REG8(0x0e38), 0x49 },
+ { CCI_REG8(0x0210), 0x13 },
+ { CCI_REG8(0x0218), 0x00 },
+ { CCI_REG8(0x0241), 0x88 },
+ { CCI_REG8(0x0392), 0x60 },
+
+ /* ISP */
+ { CCI_REG8(0x00a2), 0x00 },
+ { CCI_REG8(0x00a3), 0x00 },
+ { CCI_REG8(0x00ab), 0x00 },
+ { CCI_REG8(0x00ac), 0x00 },
+
+ /* GAIN */
+ { CCI_REG8(0x0204), 0x04 },
+ { CCI_REG8(0x0205), 0x00 },
+ { CCI_REG8(0x0050), 0x5c },
+ { CCI_REG8(0x0051), 0x44 },
+
+ /* out window */
+ { CCI_REG8(0x009a), 0x66 },
+ { CCI_REG8(0x0351), 0x00 },
+ { CCI_REG8(0x0352), 0x06 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x08 },
+ { CCI_REG8(0x034c), 0x0c },
+ { CCI_REG8(0x034d), 0xc0 },
+ { CCI_REG8(0x034e), 0x09 },
+ { CCI_REG8(0x034f), 0x90 },
+
+ /* MIPI */
+ { CCI_REG8(0x0114), 0x03 },
+ { CCI_REG8(0x0180), 0x65 },
+ { CCI_REG8(0x0181), 0xf0 },
+ { CCI_REG8(0x0185), 0x01 },
+ { CCI_REG8(0x0115), 0x30 },
+ { CCI_REG8(0x011b), 0x12 },
+ { CCI_REG8(0x011c), 0x12 },
+ { CCI_REG8(0x0121), 0x06 },
+ { CCI_REG8(0x0122), 0x06 },
+ { CCI_REG8(0x0123), 0x15 },
+ { CCI_REG8(0x0124), 0x01 },
+ { CCI_REG8(0x0125), 0x0b },
+ { CCI_REG8(0x0126), 0x08 },
+ { CCI_REG8(0x0129), 0x06 },
+ { CCI_REG8(0x012a), 0x08 },
+ { CCI_REG8(0x012b), 0x08 },
+
+ { CCI_REG8(0x0a73), 0x60 },
+ { CCI_REG8(0x0a70), 0x11 },
+ { CCI_REG8(0x0313), 0x80 },
+ { CCI_REG8(0x0aff), 0x00 },
+ { CCI_REG8(0x0a70), 0x00 },
+ { CCI_REG8(0x00a4), 0x80 },
+ { CCI_REG8(0x0316), 0x01 },
+ { CCI_REG8(0x0a67), 0x00 },
+ { CCI_REG8(0x0084), 0x10 },
+ { CCI_REG8(0x0102), 0x09 },
+};
+
+static const struct cci_reg_sequence mode_1920x1080[] = {
+ /* system */
+ { CCI_REG8(0x0336), 0x45 },
+ { CCI_REG8(0x0383), 0x8b },
+ { CCI_REG8(0x0344), 0x02 },
+ { CCI_REG8(0x0345), 0xa6 },
+ { CCI_REG8(0x0346), 0x02 },
+ { CCI_REG8(0x0347), 0xb0 },
+ { CCI_REG8(0x0348), 0x07 },
+ { CCI_REG8(0x0349), 0x90 },
+ { CCI_REG8(0x034a), 0x04 },
+ { CCI_REG8(0x034b), 0x44 },
+ { CCI_REG8(0x0202), 0x03 },
+ { CCI_REG8(0x0203), 0x00 },
+ { CCI_REG8(0x0340), 0x04 },
+ { CCI_REG8(0x0341), 0xfc },
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x1c },
+ { CCI_REG8(0x0226), 0x00 },
+ { CCI_REG8(0x0227), 0x88 },
+ { CCI_REG8(0x0e38), 0x49 },
+ { CCI_REG8(0x0210), 0x13 },
+ { CCI_REG8(0x0218), 0x00 },
+ { CCI_REG8(0x0241), 0x88 },
+ { CCI_REG8(0x0392), 0x60 },
+
+ /* ISP */
+ { CCI_REG8(0x00a2), 0xac },
+ { CCI_REG8(0x00a3), 0x02 },
+ { CCI_REG8(0x00ab), 0xa0 },
+ { CCI_REG8(0x00ac), 0x02 },
+
+ /* GAIN */
+ { CCI_REG8(0x0204), 0x04 },
+ { CCI_REG8(0x0205), 0x00 },
+ { CCI_REG8(0x0050), 0x38 },
+ { CCI_REG8(0x0051), 0x20 },
+
+ /* out window */
+ { CCI_REG8(0x009a), 0x66 },
+ { CCI_REG8(0x0351), 0x00 },
+ { CCI_REG8(0x0352), 0x06 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x08 },
+ { CCI_REG8(0x034c), 0x07 },
+ { CCI_REG8(0x034d), 0x80 },
+ { CCI_REG8(0x034e), 0x04 },
+ { CCI_REG8(0x034f), 0x38 },
+
+ /* MIPI */
+ { CCI_REG8(0x0114), 0x03 },
+ { CCI_REG8(0x0180), 0x65 },
+ { CCI_REG8(0x0181), 0xf0 },
+ { CCI_REG8(0x0185), 0x01 },
+ { CCI_REG8(0x0115), 0x30 },
+ { CCI_REG8(0x011b), 0x12 },
+ { CCI_REG8(0x011c), 0x12 },
+ { CCI_REG8(0x0121), 0x02 },
+ { CCI_REG8(0x0122), 0x03 },
+ { CCI_REG8(0x0123), 0x0c },
+ { CCI_REG8(0x0124), 0x00 },
+ { CCI_REG8(0x0125), 0x09 },
+ { CCI_REG8(0x0126), 0x06 },
+ { CCI_REG8(0x0129), 0x04 },
+ { CCI_REG8(0x012a), 0x03 },
+ { CCI_REG8(0x012b), 0x06 },
+
+ { CCI_REG8(0x0a73), 0x60 },
+ { CCI_REG8(0x0a70), 0x11 },
+ { CCI_REG8(0x0313), 0x80 },
+ { CCI_REG8(0x0aff), 0x00 },
+ { CCI_REG8(0x0a70), 0x00 },
+ { CCI_REG8(0x00a4), 0x80 },
+ { CCI_REG8(0x0316), 0x01 },
+ { CCI_REG8(0x0a67), 0x00 },
+ { CCI_REG8(0x0084), 0x10 },
+ { CCI_REG8(0x0102), 0x09 },
+};
+
+static const struct cci_reg_sequence mode_table_common[] = {
+ { GC08A3_STREAMING_REG, 0x00 },
+ /* system */
+ { CCI_REG8(0x031c), 0x60 },
+ { CCI_REG8(0x0337), 0x04 },
+ { CCI_REG8(0x0335), 0x51 },
+ { CCI_REG8(0x0336), 0x70 },
+ { CCI_REG8(0x0383), 0xbb },
+ { CCI_REG8(0x031a), 0x00 },
+ { CCI_REG8(0x0321), 0x10 },
+ { CCI_REG8(0x0327), 0x03 },
+ { CCI_REG8(0x0325), 0x40 },
+ { CCI_REG8(0x0326), 0x23 },
+ { CCI_REG8(0x0314), 0x11 },
+ { CCI_REG8(0x0315), 0xd6 },
+ { CCI_REG8(0x0316), 0x01 },
+ { CCI_REG8(0x0334), 0x40 },
+ { CCI_REG8(0x0324), 0x42 },
+ { CCI_REG8(0x031c), 0x00 },
+ { CCI_REG8(0x031c), 0x9f },
+ { CCI_REG8(0x039a), 0x13 },
+ { CCI_REG8(0x0084), 0x30 },
+ { CCI_REG8(0x02b3), 0x08 },
+ { CCI_REG8(0x0057), 0x0c },
+ { CCI_REG8(0x05c3), 0x50 },
+ { CCI_REG8(0x0311), 0x90 },
+ { CCI_REG8(0x05a0), 0x02 },
+ { CCI_REG8(0x0074), 0x0a },
+ { CCI_REG8(0x0059), 0x11 },
+ { CCI_REG8(0x0070), 0x05 },
+ { CCI_REG8(0x0101), 0x00 },
+
+ /* analog */
+ { CCI_REG8(0x0344), 0x00 },
+ { CCI_REG8(0x0345), 0x06 },
+ { CCI_REG8(0x0346), 0x00 },
+ { CCI_REG8(0x0347), 0x04 },
+ { CCI_REG8(0x0348), 0x0c },
+ { CCI_REG8(0x0349), 0xd0 },
+ { CCI_REG8(0x034a), 0x09 },
+ { CCI_REG8(0x034b), 0x9c },
+ { CCI_REG8(0x0202), 0x09 },
+ { CCI_REG8(0x0203), 0x04 },
+
+ { CCI_REG8(0x0219), 0x05 },
+ { CCI_REG8(0x0226), 0x00 },
+ { CCI_REG8(0x0227), 0x28 },
+ { CCI_REG8(0x0e0a), 0x00 },
+ { CCI_REG8(0x0e0b), 0x00 },
+ { CCI_REG8(0x0e24), 0x04 },
+ { CCI_REG8(0x0e25), 0x04 },
+ { CCI_REG8(0x0e26), 0x00 },
+ { CCI_REG8(0x0e27), 0x10 },
+ { CCI_REG8(0x0e01), 0x74 },
+ { CCI_REG8(0x0e03), 0x47 },
+ { CCI_REG8(0x0e04), 0x33 },
+ { CCI_REG8(0x0e05), 0x44 },
+ { CCI_REG8(0x0e06), 0x44 },
+ { CCI_REG8(0x0e0c), 0x1e },
+ { CCI_REG8(0x0e17), 0x3a },
+ { CCI_REG8(0x0e18), 0x3c },
+ { CCI_REG8(0x0e19), 0x40 },
+ { CCI_REG8(0x0e1a), 0x42 },
+ { CCI_REG8(0x0e28), 0x21 },
+ { CCI_REG8(0x0e2b), 0x68 },
+ { CCI_REG8(0x0e2c), 0x0d },
+ { CCI_REG8(0x0e2d), 0x08 },
+ { CCI_REG8(0x0e34), 0xf4 },
+ { CCI_REG8(0x0e35), 0x44 },
+ { CCI_REG8(0x0e36), 0x07 },
+ { CCI_REG8(0x0e38), 0x49 },
+ { CCI_REG8(0x0210), 0x13 },
+ { CCI_REG8(0x0218), 0x00 },
+ { CCI_REG8(0x0241), 0x88 },
+ { CCI_REG8(0x0e32), 0x00 },
+ { CCI_REG8(0x0e33), 0x18 },
+ { CCI_REG8(0x0e42), 0x03 },
+ { CCI_REG8(0x0e43), 0x80 },
+ { CCI_REG8(0x0e44), 0x04 },
+ { CCI_REG8(0x0e45), 0x00 },
+ { CCI_REG8(0x0e4f), 0x04 },
+ { CCI_REG8(0x057a), 0x20 },
+ { CCI_REG8(0x0381), 0x7c },
+ { CCI_REG8(0x0382), 0x9b },
+ { CCI_REG8(0x0384), 0xfb },
+ { CCI_REG8(0x0389), 0x38 },
+ { CCI_REG8(0x038a), 0x03 },
+ { CCI_REG8(0x0390), 0x6a },
+ { CCI_REG8(0x0391), 0x0b },
+ { CCI_REG8(0x0392), 0x60 },
+ { CCI_REG8(0x0393), 0xc1 },
+ { CCI_REG8(0x0396), 0xff },
+ { CCI_REG8(0x0398), 0x62 },
+
+ /* cisctl reset */
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x10 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x031c), 0x9f },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x10 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x031c), 0x9f },
+ { CCI_REG8(0x0360), 0x01 },
+ { CCI_REG8(0x0360), 0x00 },
+ { CCI_REG8(0x0316), 0x09 },
+ { CCI_REG8(0x0a67), 0x80 },
+ { CCI_REG8(0x0313), 0x00 },
+ { CCI_REG8(0x0a53), 0x0e },
+ { CCI_REG8(0x0a65), 0x17 },
+ { CCI_REG8(0x0a68), 0xa1 },
+ { CCI_REG8(0x0a58), 0x00 },
+ { CCI_REG8(0x0ace), 0x0c },
+ { CCI_REG8(0x00a4), 0x00 },
+ { CCI_REG8(0x00a5), 0x01 },
+ { CCI_REG8(0x00a7), 0x09 },
+ { CCI_REG8(0x00a8), 0x9c },
+ { CCI_REG8(0x00a9), 0x0c },
+ { CCI_REG8(0x00aa), 0xd0 },
+ { CCI_REG8(0x0a8a), 0x00 },
+ { CCI_REG8(0x0a8b), 0xe0 },
+ { CCI_REG8(0x0a8c), 0x13 },
+ { CCI_REG8(0x0a8d), 0xe8 },
+ { CCI_REG8(0x0a90), 0x0a },
+ { CCI_REG8(0x0a91), 0x10 },
+ { CCI_REG8(0x0a92), 0xf8 },
+ { CCI_REG8(0x0a71), 0xf2 },
+ { CCI_REG8(0x0a72), 0x12 },
+ { CCI_REG8(0x0a73), 0x64 },
+ { CCI_REG8(0x0a75), 0x41 },
+ { CCI_REG8(0x0a70), 0x07 },
+ { CCI_REG8(0x0313), 0x80 },
+
+ /* ISP */
+ { CCI_REG8(0x00a0), 0x01 },
+ { CCI_REG8(0x0080), 0xd2 },
+ { CCI_REG8(0x0081), 0x3f },
+ { CCI_REG8(0x0087), 0x51 },
+ { CCI_REG8(0x0089), 0x03 },
+ { CCI_REG8(0x009b), 0x40 },
+ { CCI_REG8(0x05a0), 0x82 },
+ { CCI_REG8(0x05ac), 0x00 },
+ { CCI_REG8(0x05ad), 0x01 },
+ { CCI_REG8(0x05ae), 0x00 },
+ { CCI_REG8(0x0800), 0x0a },
+ { CCI_REG8(0x0801), 0x14 },
+ { CCI_REG8(0x0802), 0x28 },
+ { CCI_REG8(0x0803), 0x34 },
+ { CCI_REG8(0x0804), 0x0e },
+ { CCI_REG8(0x0805), 0x33 },
+ { CCI_REG8(0x0806), 0x03 },
+ { CCI_REG8(0x0807), 0x8a },
+ { CCI_REG8(0x0808), 0x50 },
+ { CCI_REG8(0x0809), 0x00 },
+ { CCI_REG8(0x080a), 0x34 },
+ { CCI_REG8(0x080b), 0x03 },
+ { CCI_REG8(0x080c), 0x26 },
+ { CCI_REG8(0x080d), 0x03 },
+ { CCI_REG8(0x080e), 0x18 },
+ { CCI_REG8(0x080f), 0x03 },
+ { CCI_REG8(0x0810), 0x10 },
+ { CCI_REG8(0x0811), 0x03 },
+ { CCI_REG8(0x0812), 0x00 },
+ { CCI_REG8(0x0813), 0x00 },
+ { CCI_REG8(0x0814), 0x01 },
+ { CCI_REG8(0x0815), 0x00 },
+ { CCI_REG8(0x0816), 0x01 },
+ { CCI_REG8(0x0817), 0x00 },
+ { CCI_REG8(0x0818), 0x00 },
+ { CCI_REG8(0x0819), 0x0a },
+ { CCI_REG8(0x081a), 0x01 },
+ { CCI_REG8(0x081b), 0x6c },
+ { CCI_REG8(0x081c), 0x00 },
+ { CCI_REG8(0x081d), 0x0b },
+ { CCI_REG8(0x081e), 0x02 },
+ { CCI_REG8(0x081f), 0x00 },
+ { CCI_REG8(0x0820), 0x00 },
+ { CCI_REG8(0x0821), 0x0c },
+ { CCI_REG8(0x0822), 0x02 },
+ { CCI_REG8(0x0823), 0xd9 },
+ { CCI_REG8(0x0824), 0x00 },
+ { CCI_REG8(0x0825), 0x0d },
+ { CCI_REG8(0x0826), 0x03 },
+ { CCI_REG8(0x0827), 0xf0 },
+ { CCI_REG8(0x0828), 0x00 },
+ { CCI_REG8(0x0829), 0x0e },
+ { CCI_REG8(0x082a), 0x05 },
+ { CCI_REG8(0x082b), 0x94 },
+ { CCI_REG8(0x082c), 0x09 },
+ { CCI_REG8(0x082d), 0x6e },
+ { CCI_REG8(0x082e), 0x07 },
+ { CCI_REG8(0x082f), 0xe6 },
+ { CCI_REG8(0x0830), 0x10 },
+ { CCI_REG8(0x0831), 0x0e },
+ { CCI_REG8(0x0832), 0x0b },
+ { CCI_REG8(0x0833), 0x2c },
+ { CCI_REG8(0x0834), 0x14 },
+ { CCI_REG8(0x0835), 0xae },
+ { CCI_REG8(0x0836), 0x0f },
+ { CCI_REG8(0x0837), 0xc4 },
+ { CCI_REG8(0x0838), 0x18 },
+ { CCI_REG8(0x0839), 0x0e },
+ { CCI_REG8(0x05ac), 0x01 },
+ { CCI_REG8(0x059a), 0x00 },
+ { CCI_REG8(0x059b), 0x00 },
+ { CCI_REG8(0x059c), 0x01 },
+ { CCI_REG8(0x0598), 0x00 },
+ { CCI_REG8(0x0597), 0x14 },
+ { CCI_REG8(0x05ab), 0x09 },
+ { CCI_REG8(0x05a4), 0x02 },
+ { CCI_REG8(0x05a3), 0x05 },
+ { CCI_REG8(0x05a0), 0xc2 },
+ { CCI_REG8(0x0207), 0xc4 },
+
+ /* GAIN */
+ { CCI_REG8(0x0208), 0x01 },
+ { CCI_REG8(0x0209), 0x72 },
+ { CCI_REG8(0x0204), 0x04 },
+ { CCI_REG8(0x0205), 0x00 },
+
+ { CCI_REG8(0x0040), 0x22 },
+ { CCI_REG8(0x0041), 0x20 },
+ { CCI_REG8(0x0043), 0x10 },
+ { CCI_REG8(0x0044), 0x00 },
+ { CCI_REG8(0x0046), 0x08 },
+ { CCI_REG8(0x0047), 0xf0 },
+ { CCI_REG8(0x0048), 0x0f },
+ { CCI_REG8(0x004b), 0x0f },
+ { CCI_REG8(0x004c), 0x00 },
+ { CCI_REG8(0x0050), 0x5c },
+ { CCI_REG8(0x0051), 0x44 },
+ { CCI_REG8(0x005b), 0x03 },
+ { CCI_REG8(0x00c0), 0x00 },
+ { CCI_REG8(0x00c1), 0x80 },
+ { CCI_REG8(0x00c2), 0x31 },
+ { CCI_REG8(0x00c3), 0x00 },
+ { CCI_REG8(0x0460), 0x04 },
+ { CCI_REG8(0x0462), 0x08 },
+ { CCI_REG8(0x0464), 0x0e },
+ { CCI_REG8(0x0466), 0x0a },
+ { CCI_REG8(0x0468), 0x12 },
+ { CCI_REG8(0x046a), 0x12 },
+ { CCI_REG8(0x046c), 0x10 },
+ { CCI_REG8(0x046e), 0x0c },
+ { CCI_REG8(0x0461), 0x03 },
+ { CCI_REG8(0x0463), 0x03 },
+ { CCI_REG8(0x0465), 0x03 },
+ { CCI_REG8(0x0467), 0x03 },
+ { CCI_REG8(0x0469), 0x04 },
+ { CCI_REG8(0x046b), 0x04 },
+ { CCI_REG8(0x046d), 0x04 },
+ { CCI_REG8(0x046f), 0x04 },
+ { CCI_REG8(0x0470), 0x04 },
+ { CCI_REG8(0x0472), 0x10 },
+ { CCI_REG8(0x0474), 0x26 },
+ { CCI_REG8(0x0476), 0x38 },
+ { CCI_REG8(0x0478), 0x20 },
+ { CCI_REG8(0x047a), 0x30 },
+ { CCI_REG8(0x047c), 0x38 },
+ { CCI_REG8(0x047e), 0x60 },
+ { CCI_REG8(0x0471), 0x05 },
+ { CCI_REG8(0x0473), 0x05 },
+ { CCI_REG8(0x0475), 0x05 },
+ { CCI_REG8(0x0477), 0x05 },
+ { CCI_REG8(0x0479), 0x04 },
+ { CCI_REG8(0x047b), 0x04 },
+ { CCI_REG8(0x047d), 0x04 },
+ { CCI_REG8(0x047f), 0x04 },
+};
+
+struct gc08a3_mode {
+ u32 width;
+ u32 height;
+ const struct gc08a3_reg_list reg_list;
+
+ u32 hts; /* Horizontal timining size */
+ u32 vts_def; /* Default vertical timining size */
+ u32 vts_min; /* Min vertical timining size */
+};
+
+/* Declare modes in order, from biggest to smallest height. */
+static const struct gc08a3_mode gc08a3_modes[] = {
+ {
+ /* 3264*2448@30fps */
+ .width = GC08A3_NATIVE_WIDTH,
+ .height = GC08A3_NATIVE_HEIGHT,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_3264x2448),
+ .regs = mode_3264x2448,
+ },
+ .hts = 3640,
+ .vts_def = 2548,
+ .vts_min = 2548,
+ },
+ {
+ /* 1920*1080@60fps */
+ .width = 1920,
+ .height = 1080,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1920x1080),
+ .regs = mode_1920x1080,
+ },
+ .hts = 3640,
+ .vts_def = 1276,
+ .vts_min = 1276,
+ },
+};
+
+static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct gc08a3, sd);
+}
+
+static int gc08a3_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(gc08a3_supply_name),
+ gc08a3->supplies);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(gc08a3->xclk);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name),
+ gc08a3->supplies);
+ dev_err(gc08a3->dev, "clk prepare enable failed\n");
+ return ret;
+ }
+
+ fsleep(GC08A3_SLEEP_US);
+
+ gpiod_set_value_cansleep(gc08a3->reset_gpio, 0);
+ fsleep(GC08A3_SLEEP_US);
+
+ return 0;
+}
+
+static int gc08a3_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ clk_disable_unprepare(gc08a3->xclk);
+ gpiod_set_value_cansleep(gc08a3->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name),
+ gc08a3->supplies);
+
+ return 0;
+}
+
+static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = GC08A3_MBUS_CODE;
+
+ return 0;
+}
+
+static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->code != GC08A3_MBUS_CODE)
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(gc08a3_modes))
+ return -EINVAL;
+
+ fse->min_width = gc08a3_modes[fse->index].width;
+ fse->max_width = gc08a3_modes[fse->index].width;
+ fse->min_height = gc08a3_modes[fse->index].height;
+ fse->max_height = gc08a3_modes[fse->index].height;
+
+ return 0;
+}
+
+static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3,
+ const struct gc08a3_mode *mode)
+{
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ret = __v4l2_ctrl_modify_range(gc08a3->vblank,
+ mode->vts_min - mode->height,
+ GC08A3_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+ if (ret) {
+ dev_err(gc08a3->dev, "VB ctrl range update failed\n");
+ return ret;
+ }
+
+ h_blank = mode->hts - mode->width;
+ ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank, 1,
+ h_blank);
+ if (ret) {
+ dev_err(gc08a3->dev, "HB ctrl range update failed\n");
+ return ret;
+ }
+
+ exposure_max = mode->vts_def - GC08A3_EXP_MARGIN;
+ ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN,
+ exposure_max, GC08A3_EXP_STEP,
+ exposure_max);
+ if (ret) {
+ dev_err(gc08a3->dev, "exposure ctrl range update failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gc08a3_update_pad_format(struct gc08a3 *gc08a3,
+ const struct gc08a3_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->code = GC08A3_MBUS_CODE;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int gc08a3_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ struct v4l2_rect *crop;
+ const struct gc08a3_mode *mode;
+
+ mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes),
+ width, height, fmt->format.width,
+ fmt->format.height);
+
+ /* update crop info to subdev state */
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ crop->width = mode->width;
+ crop->height = mode->height;
+
+ /* update fmt info to subdev state */
+ gc08a3_update_pad_format(gc08a3, mode, &fmt->format);
+ mbus_fmt = v4l2_subdev_state_get_format(state, 0);
+ *mbus_fmt = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ gc08a3->cur_mode = mode;
+ gc08a3_update_cur_mode_controls(gc08a3, mode);
+
+ return 0;
+}
+
+static int gc08a3_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, 0);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC08A3_NATIVE_WIDTH;
+ sel->r.height = GC08A3_NATIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gc08a3_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ .code = GC08A3_MBUS_CODE,
+ .width = gc08a3_modes[0].width,
+ .height = gc08a3_modes[0].height,
+ },
+ };
+
+ gc08a3_set_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG,
+ GC08A3_FLIP_H_MASK,
+ ctrl_val ? GC08A3_FLIP_H_MASK : 0, NULL);
+}
+
+static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG,
+ GC08A3_FLIP_V_MASK,
+ ctrl_val ? GC08A3_FLIP_V_MASK : 0, NULL);
+}
+
+static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32 pattern_menu)
+{
+ u32 pattern;
+ int ret;
+
+ if (pattern_menu) {
+ switch (pattern_menu) {
+ case 1:
+ pattern = 0x00;
+ break;
+ case 2:
+ pattern = 0x10;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ pattern = pattern_menu + 1;
+ break;
+ default:
+ pattern = 0x00;
+ break;
+ }
+
+ ret = cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_IDX,
+ pattern, NULL);
+ if (ret)
+ return ret;
+
+ return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN,
+ GC08A3_TEST_PATTERN_EN, NULL);
+ } else {
+ return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN,
+ 0x00, NULL);
+ }
+}
+
+static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gc08a3 *gc08a3 =
+ container_of(ctrl->handler, struct gc08a3, ctrls);
+ int ret = 0;
+ s64 exposure_max;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
+
+ state = v4l2_subdev_get_locked_active_state(&gc08a3->sd);
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = format->height + ctrl->val - GC08A3_EXP_MARGIN;
+ __v4l2_ctrl_modify_range(gc08a3->exposure,
+ gc08a3->exposure->minimum,
+ exposure_max, gc08a3->exposure->step,
+ exposure_max);
+ }
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is on for streaming.
+ */
+ if (!pm_runtime_get_if_active(gc08a3->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(gc08a3->regmap, GC08A3_EXP_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(gc08a3->regmap, GC08A3_AGAIN_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_VBLANK:
+ ret = cci_write(gc08a3->regmap, GC08A3_FRAME_LENGTH_REG,
+ gc08a3->cur_mode->height + ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_HFLIP:
+ ret = gc08a3_set_ctrl_hflip(gc08a3, ctrl->val);
+ break;
+
+ case V4L2_CID_VFLIP:
+ ret = gc08a3_set_ctrl_vflip(gc08a3, ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = gc08a3_test_pattern(gc08a3, ctrl->val);
+ break;
+
+ default:
+ break;
+ }
+
+ pm_runtime_put(gc08a3->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = {
+ .s_ctrl = gc08a3_set_ctrl,
+};
+
+static int gc08a3_start_streaming(struct gc08a3 *gc08a3)
+{
+ const struct gc08a3_mode *mode;
+ const struct gc08a3_reg_list *reg_list;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(gc08a3->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = cci_multi_reg_write(gc08a3->regmap,
+ mode_table_common,
+ ARRAY_SIZE(mode_table_common), NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ mode = gc08a3->cur_mode;
+ reg_list = &mode->reg_list;
+ ret = cci_multi_reg_write(gc08a3->regmap,
+ reg_list->regs, reg_list->num_of_regs, NULL);
+ if (ret < 0)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "could not sync v4l2 controls\n");
+ goto err_rpm_put;
+ }
+
+ ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 1, NULL);
+ if (ret < 0) {
+ dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret);
+ goto err_rpm_put;
+ }
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(gc08a3->dev);
+ return ret;
+}
+
+static int gc08a3_stop_streaming(struct gc08a3 *gc08a3)
+{
+ int ret;
+
+ ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 0, NULL);
+ if (ret < 0)
+ dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret);
+
+ pm_runtime_put(gc08a3->dev);
+ return ret;
+}
+
+static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct gc08a3 *gc08a3 = to_gc08a3(subdev);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (enable)
+ ret = gc08a3_start_streaming(gc08a3);
+ else
+ ret = gc08a3_stop_streaming(gc08a3);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops gc08a3_video_ops = {
+ .s_stream = gc08a3_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = {
+ .enum_mbus_code = gc08a3_enum_mbus_code,
+ .enum_frame_size = gc08a3_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = gc08a3_set_format,
+ .get_selection = gc08a3_get_selection,
+};
+
+static const struct v4l2_subdev_core_ops gc08a3_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops gc08a3_subdev_ops = {
+ .core = &gc08a3_core_ops,
+ .video = &gc08a3_video_ops,
+ .pad = &gc08a3_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops gc08a3_internal_ops = {
+ .init_state = gc08a3_init_state,
+};
+
+static int gc08a3_get_regulators(struct device *dev, struct gc08a3 *gc08a3)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(gc08a3_supply_name); i++)
+ gc08a3->supplies[i].supply = gc08a3_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc08a3_supply_name),
+ gc08a3->supplies);
+}
+
+static int gc08a3_parse_fwnode(struct gc08a3 *gc08a3)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ int ret;
+ struct device *dev = gc08a3->dev;
+
+ endpoint =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ if (ret) {
+ dev_err(dev, "parsing endpoint node failed\n");
+ goto done;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ gc08a3_link_freq_menu_items,
+ ARRAY_SIZE(gc08a3_link_freq_menu_items),
+ &gc08a3->link_freq_bitmap);
+ if (ret)
+ goto done;
+
+done:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ fwnode_handle_put(endpoint);
+ return ret;
+}
+
+static u64 gc08a3_to_pixel_rate(u32 f_index)
+{
+ u64 pixel_rate =
+ gc08a3_link_freq_menu_items[f_index] * 2 * GC08A3_DATA_LANES;
+
+ return div_u64(pixel_rate, GC08A3_RGB_DEPTH);
+}
+
+static int gc08a3_init_controls(struct gc08a3 *gc08a3)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd);
+ const struct gc08a3_mode *mode = &gc08a3_modes[0];
+ const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ctrl_hdlr = &gc08a3->ctrls;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+ if (ret)
+ return ret;
+
+ gc08a3->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ gc08a3->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &gc08a3->hflip);
+
+ gc08a3->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &gc08a3_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(gc08a3_link_freq_menu_items) - 1,
+ 0,
+ gc08a3_link_freq_menu_items);
+ if (gc08a3->link_freq)
+ gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ gc08a3->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc08a3_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ gc08a3_to_pixel_rate(0),
+ 1,
+ gc08a3_to_pixel_rate(0));
+
+ gc08a3->vblank =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc08a3_ctrl_ops, V4L2_CID_VBLANK,
+ mode->vts_min - mode->height,
+ GC08A3_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ h_blank = mode->hts - mode->width;
+ gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_HBLANK, h_blank, h_blank, 1,
+ h_blank);
+ if (gc08a3->hblank)
+ gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN,
+ GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP,
+ GC08A3_AGAIN_MIN);
+
+ exposure_max = mode->vts_def - GC08A3_EXP_MARGIN;
+ gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_EXPOSURE, GC08A3_EXP_MIN,
+ exposure_max, GC08A3_EXP_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(gc08a3_test_pattern_menu) - 1,
+ 0, 0, gc08a3_test_pattern_menu);
+
+ /* register properties to fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
+ if (ret)
+ goto error_ctrls;
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto error_ctrls;
+ }
+
+ gc08a3->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_ctrls:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int gc08a3_identify_module(struct gc08a3 *gc08a3)
+{
+ u64 val;
+ int ret;
+
+ ret = cci_read(gc08a3->regmap, GC08A3_REG_CHIP_ID, &val, NULL);
+ if (ret) {
+ dev_err(gc08a3->dev, "failed to read chip id");
+ return ret;
+ }
+
+ if (val != GC08A3_CHIP_ID) {
+ dev_err(gc08a3->dev, "chip id mismatch: 0x%x!=0x%llx",
+ GC08A3_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int gc08a3_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gc08a3 *gc08a3;
+ int ret;
+
+ gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL);
+ if (!gc08a3)
+ return -ENOMEM;
+
+ gc08a3->dev = dev;
+
+ ret = gc08a3_parse_fwnode(gc08a3);
+ if (ret)
+ return ret;
+
+ gc08a3->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(gc08a3->regmap))
+ return dev_err_probe(dev, PTR_ERR(gc08a3->regmap),
+ "failed to init CCI\n");
+
+ gc08a3->xclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(gc08a3->xclk))
+ return dev_err_probe(dev, PTR_ERR(gc08a3->xclk),
+ "failed to get xclk\n");
+
+ ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set xclk frequency\n");
+
+ ret = gc08a3_get_regulators(dev, gc08a3);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get regulators\n");
+
+ gc08a3->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gc08a3->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(gc08a3->reset_gpio),
+ "failed to get gpio\n");
+
+ v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops);
+ gc08a3->sd.internal_ops = &gc08a3_internal_ops;
+ gc08a3->cur_mode = &gc08a3_modes[0];
+
+ ret = gc08a3_power_on(gc08a3->dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to sensor power on\n");
+
+ ret = gc08a3_identify_module(gc08a3);
+ if (ret) {
+ dev_err(&client->dev, "failed to find sensor: %d\n", ret);
+ goto err_power_off;
+ }
+
+ ret = gc08a3_init_controls(gc08a3);
+ if (ret) {
+ dev_err(&client->dev, "failed to init controls: %d", ret);
+ goto err_power_off;
+ }
+
+ gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE;
+ gc08a3->sd.dev = &client->dev;
+ gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad);
+ if (ret < 0) {
+ dev_err(dev, "could not register media entity\n");
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ gc08a3->sd.state_lock = gc08a3->ctrls.lock;
+ ret = v4l2_subdev_init_finalize(&gc08a3->sd);
+ if (ret < 0) {
+ dev_err(dev, "v4l2 subdev init error: %d\n", ret);
+ goto err_media_entity_cleanup;
+ }
+
+ pm_runtime_set_active(gc08a3->dev);
+ pm_runtime_enable(gc08a3->dev);
+ pm_runtime_set_autosuspend_delay(gc08a3->dev, 1000);
+ pm_runtime_use_autosuspend(gc08a3->dev);
+ pm_runtime_idle(gc08a3->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&gc08a3->sd);
+ if (ret < 0) {
+ dev_err(dev, "could not register v4l2 device\n");
+ goto err_rpm;
+ }
+
+ return 0;
+
+err_rpm:
+ pm_runtime_disable(gc08a3->dev);
+ v4l2_subdev_cleanup(&gc08a3->sd);
+
+err_media_entity_cleanup:
+ media_entity_cleanup(&gc08a3->sd.entity);
+
+err_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(gc08a3->sd.ctrl_handler);
+
+err_power_off:
+ gc08a3_power_off(gc08a3->dev);
+
+ return ret;
+}
+
+static void gc08a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc08a3 *gc08a3 = to_gc08a3(sd);
+
+ v4l2_async_unregister_subdev(&gc08a3->sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&gc08a3->sd.entity);
+ v4l2_ctrl_handler_free(&gc08a3->ctrls);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ gc08a3_power_off(gc08a3->dev);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct of_device_id gc08a3_of_match[] = {
+ { .compatible = "galaxycore,gc08a3" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gc08a3_of_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(gc08a3_pm_ops,
+ gc08a3_power_off,
+ gc08a3_power_on,
+ NULL);
+
+static struct i2c_driver gc08a3_i2c_driver = {
+ .driver = {
+ .of_match_table = gc08a3_of_match,
+ .pm = pm_ptr(&gc08a3_pm_ops),
+ .name = "gc08a3",
+ },
+ .probe = gc08a3_probe,
+ .remove = gc08a3_remove,
+};
+module_i2c_driver(gc08a3_i2c_driver);
+
+MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver");
+MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>");
+MODULE_LICENSE("GPL");
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v8 1/2] media: dt-bindings: i2c: add GalaxyCore GC08A3 image sensor
From: Zhi Mao @ 2024-03-23 2:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek,
Krzysztof Kozlowski
In-Reply-To: <20240323023851.5503-1-zhi.mao@mediatek.com>
Add YAML device tree binding for GC08A3 CMOS image sensor,
and the relevant MAINTAINERS entries.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
.../bindings/media/i2c/galaxycore,gc08a3.yaml | 112 ++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
new file mode 100644
index 000000000000..51b8ece09c72
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (c) 2023 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor
+
+maintainers:
+ - Zhi Mao <zhi.mao@mediatek.com>
+
+description:
+ The gc08a3 is a raw image sensor with an MIPI CSI-2 image data
+ interface and CCI (I2C compatible) control bus. The output format
+ is raw Bayer.
+
+properties:
+ compatible:
+ const: galaxycore,gc08a3
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dovdd-supply: true
+
+ avdd-supply: true
+
+ dvdd-supply: true
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the RESETB pin.
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+ description:
+ Output port node, single endpoint describing the CSI-2 transmitter.
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ oneOf:
+ - items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+ - items:
+ - const: 1
+ - const: 2
+
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - dovdd-supply
+ - avdd-supply
+ - dvdd-supply
+ - reset-gpios
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@31 {
+ compatible = "galaxycore,gc08a3";
+ reg = <0x31>;
+
+ clocks = <&gc08a3_clk>;
+
+ reset-gpios = <&pio 19 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&gc08a3_avdd>;
+ dovdd-supply = <&gc08a3_dovdd>;
+ dvdd-supply = <&gc08a3_dvdd>;
+
+ port {
+ sensor_out: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <336000000 207000000>;
+ remote-endpoint = <&seninf_csi_port_0_in>;
+ };
+ };
+ };
+ };
+
+...
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v8 0/2] media: i2c: Add support for GC08A3 sensor
From: Zhi Mao @ 2024-03-23 2:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
This series adds YAML DT binding and V4L2 sub-device driver for Galaxycore's
GC08A3 8-megapixel 10-bit RAW CMOS 1/4" sensor, with an MIPI CSI-2 image data
interface and the I2C control bus.
The driver is implemented with V4L2 framework.
- Async registered as a V4L2 sub-device.
- As the first component of camera system including Seninf, ISP pipeline.
- A media entity that provides one source pad in common.
- Used in camera features on ChromeOS application.
Also this driver supports following features:
- manual exposure and analog gain control support
- vertical blanking control support
- test pattern support
- media controller support
- runtime PM support
- support resolution: 3264x2448@30fps, 1920x1080@60fps
Previous versions of this patch-set can be found here:
v7: https://lore.kernel.org/linux-media/20240303022609.26263-1-zhi.mao@mediatek.com/
v6: https://lore.kernel.org/linux-media/20240227013221.21512-1-zhi.mao@mediatek.com/
v5: https://lore.kernel.org/linux-media/20240220012540.10607-1-zhi.mao@mediatek.com/
v4: https://lore.kernel.org/linux-media/20240204061538.2105-1-zhi.mao@mediatek.com/
v3: https://lore.kernel.org/linux-media/20240109022715.30278-1-zhi.mao@mediatek.com/
v2: https://lore.kernel.org/linux-media/20231207052016.25954-1-zhi.mao@mediatek.com/
v1: https://lore.kernel.org/linux-media/20231123115104.32094-1-zhi.mao@mediatek.com/
This series is based on linux-next, tag: next-20240323
Changes in v8:
- gc08a3 sensor driver:
-- use function: pm_runtime_get_if_active()
Thanks
Zhi Mao (2):
media: dt-bindings: i2c: add GalaxyCore GC08A3 image sensor
media: i2c: Add GC08A3 image sensor driver
.../bindings/media/i2c/galaxycore,gc08a3.yaml | 112 ++
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc08a3.c | 1339 +++++++++++++++++
4 files changed, 1462 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml
create mode 100644 drivers/media/i2c/gc08a3.c
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Kent Overstreet @ 2024-03-23 2:33 UTC (permalink / raw)
To: Boqun Feng
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <Zf491DuptReGqvfd@Boquns-Mac-mini.home>
On Fri, Mar 22, 2024 at 07:26:28PM -0700, Boqun Feng wrote:
> On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
> [...]
> > > Boqun already mentioned the "mixing access sizes", which is actually
> > > quite fundamental in the kernel, where we play lots of games with that
> > > (typically around locking, where you find patterns line unlock writing
> > > a zero to a single byte, even though the whole lock data structure is
> > > a word). And sometimes the access size games are very explicit (eg
> > > lib/lockref.c).
> >
> > I don't think mixing access sizes should be a real barrier. On the read
>
> Well, it actually is, since mixing access sizes is, guess what,
> an undefined behavior:
>
> (example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
>
> thread::scope(|s| {
> // This is UB: using different-sized atomic accesses to the same data
> s.spawn(|| atomic.store(1, Ordering::Relaxed));
> s.spawn(|| unsafe {
> let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
> differently_sized.store(2, Ordering::Relaxed);
> });
> });
>
> Of course, you can say "I will just ignore the UB", but if you have to
> ignore "compiler rules" to make your code work, why bother use compiler
> builtin in the first place? Being UB means they are NOT guaranteed to
> work.
That's not what I'm proposing - you'd need additional compiler support.
but the new intrinsic would be no different, semantics wise for the
compiler to model, than a "lock orb".
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Boqun Feng @ 2024-03-23 2:26 UTC (permalink / raw)
To: Kent Overstreet
Cc: Linus Torvalds, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <3modld2dafaqjxa2b7jln47ws4ylzhbsvhvnphoklwvzange5p@wlir7276aitp>
On Fri, Mar 22, 2024 at 10:07:31PM -0400, Kent Overstreet wrote:
[...]
> > Boqun already mentioned the "mixing access sizes", which is actually
> > quite fundamental in the kernel, where we play lots of games with that
> > (typically around locking, where you find patterns line unlock writing
> > a zero to a single byte, even though the whole lock data structure is
> > a word). And sometimes the access size games are very explicit (eg
> > lib/lockref.c).
>
> I don't think mixing access sizes should be a real barrier. On the read
Well, it actually is, since mixing access sizes is, guess what,
an undefined behavior:
(example in https://doc.rust-lang.org/std/sync/atomic/#memory-model-for-atomic-accesses)
thread::scope(|s| {
// This is UB: using different-sized atomic accesses to the same data
s.spawn(|| atomic.store(1, Ordering::Relaxed));
s.spawn(|| unsafe {
let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
differently_sized.store(2, Ordering::Relaxed);
});
});
Of course, you can say "I will just ignore the UB", but if you have to
ignore "compiler rules" to make your code work, why bother use compiler
builtin in the first place? Being UB means they are NOT guaranteed to
work.
> side we can obviously do that with a helper; the write side needs
> compiler help, but "writing just a byte out of a word" is no different
> from a compiler POV that "write a single bit", and we can already mix
> atomic_or() with atomic_add(), with both C atomics and LKMM atomics.
>
I totally agree with your reasoning here, but maybe the standard doesn't
;-)
Regards,
Boqun
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [RFC PATCH v3 1/5] arm64: mm: swap: support THP_SWAP on hardware with MTE
From: Chris Li @ 2024-03-23 2:15 UTC (permalink / raw)
To: Ryan Roberts
Cc: Barry Song, akpm, linux-mm, chengming.zhou, david, hannes, kasong,
linux-arm-kernel, linux-kernel, mhocko, nphamcs, shy828301,
steven.price, surenb, wangkefeng.wang, willy, xiang, ying.huang,
yosryahmed, yuzhao, Barry Song, Catalin Marinas, Will Deacon,
Mark Rutland, Kemeng Shi, Anshuman Khandual, Peter Collingbourne,
Peter Xu, Lorenzo Stoakes, Mike Rapoport (IBM), Hugh Dickins,
Aneesh Kumar K.V, Rick Edgecombe
In-Reply-To: <2e9ddc16-b6e7-46d4-8dd7-61977f61f2d0@arm.com>
On Fri, Mar 22, 2024 at 3:19 AM Ryan Roberts <ryan.roberts@arm.com> wrote:
>
> On 22/03/2024 07:41, Barry Song wrote:
> > On Fri, Mar 22, 2024 at 3:51 PM Barry Song <21cnbao@gmail.com> wrote:
> >>
> >> On Thu, Mar 21, 2024 at 11:31 PM Ryan Roberts <ryan.roberts@arm.com> wrote:
> >>>
> >>> On 21/03/2024 08:42, Barry Song wrote:
> >>>> Hi Ryan,
> >>>> Sorry for the late reply.
> >>>
> >>> No problem!
> >>>
> >>>>
> >>>> On Tue, Mar 12, 2024 at 5:56 AM Ryan Roberts <ryan.roberts@arm.com> wrote:
> >>>>>
> >>>>> On 04/03/2024 08:13, Barry Song wrote:
> >>>>>> From: Barry Song <v-songbaohua@oppo.com>
> >>>>>>
> >>>>>> Commit d0637c505f8a1 ("arm64: enable THP_SWAP for arm64") brings up
> >>>>>> THP_SWAP on ARM64, but it doesn't enable THP_SWP on hardware with
> >>>>>> MTE as the MTE code works with the assumption tags save/restore is
> >>>>>> always handling a folio with only one page.
> >>>>>>
> >>>>>> The limitation should be removed as more and more ARM64 SoCs have
> >>>>>> this feature. Co-existence of MTE and THP_SWAP becomes more and
> >>>>>> more important.
> >>>>>>
> >>>>>> This patch makes MTE tags saving support large folios, then we don't
> >>>>>> need to split large folios into base pages for swapping out on ARM64
> >>>>>> SoCs with MTE any more.
> >>>>>>
> >>>>>> arch_prepare_to_swap() should take folio rather than page as parameter
> >>>>>> because we support THP swap-out as a whole. It saves tags for all
> >>>>>> pages in a large folio.
> >>>>>>
> >>>>>> As now we are restoring tags based-on folio, in arch_swap_restore(),
> >>>>>> we may increase some extra loops and early-exitings while refaulting
> >>>>>> a large folio which is still in swapcache in do_swap_page(). In case
> >>>>>> a large folio has nr pages, do_swap_page() will only set the PTE of
> >>>>>> the particular page which is causing the page fault.
> >>>>>> Thus do_swap_page() runs nr times, and each time, arch_swap_restore()
> >>>>>> will loop nr times for those subpages in the folio. So right now the
> >>>>>> algorithmic complexity becomes O(nr^2).
> >>>>>>
> >>>>>> Once we support mapping large folios in do_swap_page(), extra loops
> >>>>>> and early-exitings will decrease while not being completely removed
> >>>>>> as a large folio might get partially tagged in corner cases such as,
> >>>>>> 1. a large folio in swapcache can be partially unmapped, thus, MTE
> >>>>>> tags for the unmapped pages will be invalidated;
> >>>>>> 2. users might use mprotect() to set MTEs on a part of a large folio.
> >>>>>>
> >>>>>> arch_thp_swp_supported() is dropped since ARM64 MTE was the only one
> >>>>>> who needed it.
> >>>
> >>> I think we should decouple this patch from your swap-in series. I suspect this
> >>> one could be ready and go in sooner than the swap-in series based on the current
> >>> discussions :)
> >>>
> >>>>>>
> >>>>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
> >>>>>> Cc: Will Deacon <will@kernel.org>
> >>>>>> Cc: Ryan Roberts <ryan.roberts@arm.com>
> >>>>>> Cc: Mark Rutland <mark.rutland@arm.com>
> >>>>>> Cc: David Hildenbrand <david@redhat.com>
> >>>>>> Cc: Kemeng Shi <shikemeng@huaweicloud.com>
> >>>>>> Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
> >>>>>> Cc: Anshuman Khandual <anshuman.khandual@arm.com>
> >>>>>> Cc: Peter Collingbourne <pcc@google.com>
> >>>>>> Cc: Steven Price <steven.price@arm.com>
> >>>>>> Cc: Yosry Ahmed <yosryahmed@google.com>
> >>>>>> Cc: Peter Xu <peterx@redhat.com>
> >>>>>> Cc: Lorenzo Stoakes <lstoakes@gmail.com>
> >>>>>> Cc: "Mike Rapoport (IBM)" <rppt@kernel.org>
> >>>>>> Cc: Hugh Dickins <hughd@google.com>
> >>>>>> CC: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
> >>>>>> Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
> >>>>>> Signed-off-by: Barry Song <v-songbaohua@oppo.com>
> >>>>>> Reviewed-by: Steven Price <steven.price@arm.com>
> >>>>>> Acked-by: Chris Li <chrisl@kernel.org>
> >>>>>> ---
> >>>>>> arch/arm64/include/asm/pgtable.h | 19 ++------------
> >>>>>> arch/arm64/mm/mteswap.c | 43 ++++++++++++++++++++++++++++++++
> >>>>>> include/linux/huge_mm.h | 12 ---------
> >>>>>> include/linux/pgtable.h | 2 +-
> >>>>>> mm/page_io.c | 2 +-
> >>>>>> mm/swap_slots.c | 2 +-
> >>>>>> 6 files changed, 48 insertions(+), 32 deletions(-)
> >>>>>>
> >>>>>> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> >>>>>> index 401087e8a43d..7a54750770b8 100644
> >>>>>> --- a/arch/arm64/include/asm/pgtable.h
> >>>>>> +++ b/arch/arm64/include/asm/pgtable.h
> >>>>>> @@ -45,12 +45,6 @@
> >>>>>> __flush_tlb_range(vma, addr, end, PUD_SIZE, false, 1)
> >>>>>> #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
> >>>>>>
> >>>>>> -static inline bool arch_thp_swp_supported(void)
> >>>>>> -{
> >>>>>> - return !system_supports_mte();
> >>>>>> -}
> >>>>>> -#define arch_thp_swp_supported arch_thp_swp_supported
> >>>>>> -
> >>>>>> /*
> >>>>>> * Outside of a few very special situations (e.g. hibernation), we always
> >>>>>> * use broadcast TLB invalidation instructions, therefore a spurious page
> >>>>>> @@ -1095,12 +1089,7 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
> >>>>>> #ifdef CONFIG_ARM64_MTE
> >>>>>>
> >>>>>> #define __HAVE_ARCH_PREPARE_TO_SWAP
> >>>>>> -static inline int arch_prepare_to_swap(struct page *page)
> >>>>>> -{
> >>>>>> - if (system_supports_mte())
> >>>>>> - return mte_save_tags(page);
> >>>>>> - return 0;
> >>>>>> -}
> >>>>>> +extern int arch_prepare_to_swap(struct folio *folio);
> >>>>>>
> >>>>>> #define __HAVE_ARCH_SWAP_INVALIDATE
> >>>>>> static inline void arch_swap_invalidate_page(int type, pgoff_t offset)
> >>>>>> @@ -1116,11 +1105,7 @@ static inline void arch_swap_invalidate_area(int type)
> >>>>>> }
> >>>>>>
> >>>>>> #define __HAVE_ARCH_SWAP_RESTORE
> >>>>>> -static inline void arch_swap_restore(swp_entry_t entry, struct folio *folio)
> >>>>>> -{
> >>>>>> - if (system_supports_mte())
> >>>>>> - mte_restore_tags(entry, &folio->page);
> >>>>>> -}
> >>>>>> +extern void arch_swap_restore(swp_entry_t entry, struct folio *folio);
> >>>>>>
> >>>>>> #endif /* CONFIG_ARM64_MTE */
> >>>>>>
> >>>>>> diff --git a/arch/arm64/mm/mteswap.c b/arch/arm64/mm/mteswap.c
> >>>>>> index a31833e3ddc5..295836fef620 100644
> >>>>>> --- a/arch/arm64/mm/mteswap.c
> >>>>>> +++ b/arch/arm64/mm/mteswap.c
> >>>>>> @@ -68,6 +68,13 @@ void mte_invalidate_tags(int type, pgoff_t offset)
> >>>>>> mte_free_tag_storage(tags);
> >>>>>> }
> >>>>>>
> >>>>>> +static inline void __mte_invalidate_tags(struct page *page)
> >>>>>> +{
> >>>>>> + swp_entry_t entry = page_swap_entry(page);
> >>>>>> +
> >>>>>> + mte_invalidate_tags(swp_type(entry), swp_offset(entry));
> >>>>>> +}
> >>>>>> +
> >>>>>> void mte_invalidate_tags_area(int type)
> >>>>>> {
> >>>>>> swp_entry_t entry = swp_entry(type, 0);
> >>>>>> @@ -83,3 +90,39 @@ void mte_invalidate_tags_area(int type)
> >>>>>> }
> >>>>>> xa_unlock(&mte_pages);
> >>>>>> }
> >>>>>> +
> >>>>>> +int arch_prepare_to_swap(struct folio *folio)
> >>>>>> +{
> >>>>>> + long i, nr;
> >>>>>> + int err;
> >>>>>> +
> >>>>>> + if (!system_supports_mte())
> >>>>>> + return 0;
> >>>>>> +
> >>>>>> + nr = folio_nr_pages(folio);
> >>>>>> +
> >>>>>> + for (i = 0; i < nr; i++) {
> >>>>>> + err = mte_save_tags(folio_page(folio, i));
> >>>>>> + if (err)
> >>>>>> + goto out;
> >>>>>> + }
> >>>>>> + return 0;
> >>>>>> +
> >>>>>> +out:
> >>>>>> + while (i--)
> >>>>>> + __mte_invalidate_tags(folio_page(folio, i));
> >>>>>> + return err;
> >>>>>> +}
> >>>>>> +
> >>>>>> +void arch_swap_restore(swp_entry_t entry, struct folio *folio)
> >>>>>
> >>>>> I'm still not a fan of the fact that entry could be anywhere within folio.
> >>>>>
> >>>>>> +{
> >>>>>> + if (system_supports_mte()) {
> >>>>>
> >>>>> nit: if you do:
> >>>>>
> >>>>> if (!system_supports_mte())
> >>>>> return;
> >>>>
> >>>> Acked
> >>>>
> >>>>>
> >>>>> It will be consistent with arch_prepare_to_swap() and reduce the indentation of
> >>>>> the main body.
> >>>>>
> >>>>>> + long i, nr = folio_nr_pages(folio);
> >>>>>> +
> >>>>>> + entry.val -= swp_offset(entry) & (nr - 1);
> >>>>>
> >>>>> This assumes that folios are always stored in swap with natural alignment. Is
> >>>>> that definitely a safe assumption? My swap-out series is currently ensuring that
> >>>>> folios are swapped-out naturally aligned, but that is an implementation detail.
> >>>>>
> >>>>
> >>>> I concur that this is an implementation detail. However, we should be
> >>>> bold enough
> >>>> to state that swap slots will be contiguous, considering we are
> >>>> currently utilizing
> >>>> folio->swap instead of subpage->swap ?
> >>>
> >>> Yes, I agree about contiguity. My objection is about assuming natural alignment
> >>> though. It can still be contiguous while not naturally aligned in swap.
> >>
> >> Hi Ryan,
> >>
> >> While working on the new version of this patch, I've come to recognize
> >> that, for the time being, it's
> >> imperative to maintain a natural alignment. The following code
> >> operates on the basis of this
> >> assumption.
> >>
> >> /**
> >> * folio_file_page - The page for a particular index.
> >> * @folio: The folio which contains this index.
> >> * @index: The index we want to look up.
> >> *
> >> * Sometimes after looking up a folio in the page cache, we need to
> >> * obtain the specific page for an index (eg a page fault).
> >> *
> >> * Return: The page containing the file data for this index.
> >> */
> >> static inline struct page *folio_file_page(struct folio *folio, pgoff_t index)
> >> {
> >> return folio_page(folio, index & (folio_nr_pages(folio) - 1));
> >> }
> >>
> >>
> >> It's invoked everywhere, particularly within do_swap_page(). Nonetheless,
> >> I remain confident that I can consistently pass the first entry to
> >> arch_swap_restore().
> >
> > After grappling for a couple of hours, I've realized that the only
> > viable approach
> > is as follows: shifting the task of obtaining the first entry from the
> > callee to the
> > callers( looks silly). This is necessary due to various scenarios like
> > swap cache,
> > non-swap cache, and KSM, each presenting different cases. Since there's no
> > assurance of folio->swap being present, forcibly setting folio->swap could pose
> > risks (There might not even be any risk involved, but the associated
> > task getting
> > the first entry still cannot be overlooked by callers).
> >
> > diff --git a/mm/internal.h b/mm/internal.h
> > index 7e486f2c502c..94d5b4b5a5da 100644
> > --- a/mm/internal.h
> > +++ b/mm/internal.h
> > @@ -76,6 +76,20 @@ static inline int folio_nr_pages_mapped(struct folio *folio)
> > return atomic_read(&folio->_nr_pages_mapped) & FOLIO_PAGES_MAPPED;
> > }
> >
> > +/*
> > + * Retrieve the first entry of a folio based on a provided entry within the
> > + * folio. We cannot rely on folio->swap as there is no guarantee that it has
> > + * been initialized. Used by arch_swap_restore()
> > + */
> > +static inline swp_entry_t folio_swap(swp_entry_t entry, struct folio *folio)
> > +{
> > + swp_entry_t swap = {
> > + .val = entry.val & (folio_nr_pages(folio) - 1),
> > + };
> > +
> > + return swap;
> > +}
> > +
> > static inline void *folio_raw_mapping(struct folio *folio)
> > {
> > unsigned long mapping = (unsigned long)folio->mapping;
> > diff --git a/mm/memory.c b/mm/memory.c
> > index f2bc6dd15eb8..b7cab8be8632 100644
> > --- a/mm/memory.c
> > +++ b/mm/memory.c
> > @@ -4188,7 +4188,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> > * when reading from swap. This metadata may be indexed by swap entry
> > * so this must be called before swap_free().
> > */
> > - arch_swap_restore(entry, folio);
> > + arch_swap_restore(folio_swap(entry, folio), folio);
> >
> > /*
> > * Remove the swap entry and conditionally try to free up the swapcache.
> > diff --git a/mm/shmem.c b/mm/shmem.c
> > index 0aad0d9a621b..82c9df4628f2 100644
> > --- a/mm/shmem.c
> > +++ b/mm/shmem.c
> > @@ -1913,7 +1913,7 @@ static int shmem_swapin_folio(struct inode
> > *inode, pgoff_t index,
> > * Some architectures may have to restore extra metadata to the
> > * folio after reading from swap.
> > */
> > - arch_swap_restore(swap, folio);
> > + arch_swap_restore(folio_swap(entry, folio), folio);
> >
> > if (shmem_should_replace_folio(folio, gfp)) {
> > error = shmem_replace_folio(&folio, gfp, info, index);
> > diff --git a/mm/swapfile.c b/mm/swapfile.c
> > index 4919423cce76..5e6d2304a2a4 100644
> > --- a/mm/swapfile.c
> > +++ b/mm/swapfile.c
> > @@ -1806,7 +1806,7 @@ static int unuse_pte(struct vm_area_struct *vma,
> > pmd_t *pmd,
> > * when reading from swap. This metadata may be indexed by swap entry
> > * so this must be called before swap_free().
> > */
> > - arch_swap_restore(entry, folio);
> > + arch_swap_restore(folio_swap(entry, folio), folio);
> >
> > dec_mm_counter(vma->vm_mm, MM_SWAPENTS);
> > inc_mm_counter(vma->vm_mm, MM_ANONPAGES);
> >
> >
> > Meanwhile, natural alignment is essential even during the execution of
> > add_to_swap(), as failure to
> > do so will trigger the VM_BUG_ON condition below.
> >
> > int add_to_swap_cache(struct folio *folio, swp_entry_t entry,
> > gfp_t gfp, void **shadowp)
> > {
> > struct address_space *address_space = swap_address_space(entry);
> > pgoff_t idx = swp_offset(entry);
> > XA_STATE_ORDER(xas, &address_space->i_pages, idx, folio_order(folio));
> > unsigned long i, nr = folio_nr_pages(folio);
> > ...
> > folio_set_swapcache(folio);
> > folio->swap = entry;
> >
> > do {
> > xas_lock_irq(&xas);
> > xas_create_range(&xas);
> > if (xas_error(&xas))
> > goto unlock;
> > for (i = 0; i < nr; i++) {
> > VM_BUG_ON_FOLIO(xas.xa_index != idx + i, folio);
Here swap_cache assue swap entry + i match folio + i subpage. The swap
entry of a folio must be continuous. If we want to allow folio write
out to the discontiguous offset of the swap device, this aspect of the
swap cache will need to change as well. Do you see a problem having
all pte entries of a folio point to the same large swap entry? Of
course, the large swap entry internally will track the offset of sub
page + i. The swap cache will only have one index for the large swap
entry (the head entry).
> > if (shadowp) {
> > old = xas_load(&xas);
> > if (xa_is_value(old))
> > *shadowp = old;
> > }
> > xas_store(&xas, folio);
> > xas_next(&xas);
> > }
> > }
> >
> >
> > Based on the information provided, Ryan, would it be feasible to retain the task
> > of obtaining the first entry within the callee? Or, are you in favor
> > of utilizing the
> > new folio_swap() helper?
>
> My opinion still remains that either:
>
> - This should be a per-page interface - i.e. call it for each page to restore
> tags. If we don't want to pass `struct page *` then perhaps we can pass a folio
Can you clarify that by "tag" you mean the MTE tags, not swap cache
xarray tags, right? From the email context I assume that is the MTE
tag. Please let me know if I assume incorrectly.
> and the index of the page we want to restore? In this case, entry refers the the
> precise page we are operating on.
>
> OR
>
> - Make it a per-folio interface - i.e. it restores tags for all pages in the
> folio. But in this case, entry must refer to the first page in the folio.
> Anything else is confusing.
As long as you refer to the subpage as folilo + i, restoring a subset
of the folio should be permitted?
On the swap entry side, I would like to avoid assuming the swap entry
is contingues. The swap entry should have an API to fetch the swap
offset of the head entry + i. For the simple continuous swap entry,
this mapping is just linear. For non continuous swap offset, it would
need to go through some lookup table to find the offset for i.
Chris
>
> So if going for the latter approach, then I vote for fixing it up in the callee.
> But I'm just one guy with one opinion!
>
>
> >
> >>
> >>>
> >>>>
> >>>>> Your cover note for swap-in says that you could technically swap in a large
> >>>>> folio without it having been swapped-out large. If you chose to do that in
> >>>>> future, this would break, right? I don't think it's good to couple the swap
> >>>>
> >>>> Right. technically I agree. Given that we still have many tasks involving even
> >>>> swapping in contiguous swap slots, it's unlikely that swapping in large folios
> >>>> for non-contiguous entries will occur in the foreseeable future :-)
> >>>>
> >>>>> storage layout to the folio order that you want to swap into. Perhaps that's an
> >>>>> argument for passing each *page* to this function with its exact, corresponding
> >>>>> swap entry?
> >>>>
> >>>> I recall Matthew Wilcox strongly objected to using "page" as the
> >>>> parameter, so I've
> >>>> discarded that approach. Alternatively, it appears I can consistently pass
> >>>> folio->swap to this function and ensure the function always retrieves
> >>>> the first entry?
> >>>
> >>> Yes, if we must pass a folio here, I'd prefer that entry always corresponds to
> >>> the first entry for the folio. That will remove the need for this function to do
> >>> the alignment above too. So win-win.
> >>>
> >>>>
> >>>>>
> >>>>>> + for (i = 0; i < nr; i++) {
> >>>>>> + mte_restore_tags(entry, folio_page(folio, i));
> >>>>>> + entry.val++;
> >>>>>> + }
> >>>>>> + }
> >>>>>> +}
> >>>>>> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> >>>>>> index de0c89105076..e04b93c43965 100644
> >>>>>> --- a/include/linux/huge_mm.h
> >>>>>> +++ b/include/linux/huge_mm.h
> >>>>>> @@ -535,16 +535,4 @@ static inline int split_folio_to_order(struct folio *folio, int new_order)
> >>>>>> #define split_folio_to_list(f, l) split_folio_to_list_to_order(f, l, 0)
> >>>>>> #define split_folio(f) split_folio_to_order(f, 0)
> >>>>>>
> >>>>>> -/*
> >>>>>> - * archs that select ARCH_WANTS_THP_SWAP but don't support THP_SWP due to
> >>>>>> - * limitations in the implementation like arm64 MTE can override this to
> >>>>>> - * false
> >>>>>> - */
> >>>>>> -#ifndef arch_thp_swp_supported
> >>>>>> -static inline bool arch_thp_swp_supported(void)
> >>>>>> -{
> >>>>>> - return true;
> >>>>>> -}
> >>>>>> -#endif
> >>>>>> -
> >>>>>> #endif /* _LINUX_HUGE_MM_H */
> >>>>>> diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
> >>>>>> index e1b22903f709..bfcfe3386934 100644
> >>>>>> --- a/include/linux/pgtable.h
> >>>>>> +++ b/include/linux/pgtable.h
> >>>>>> @@ -1106,7 +1106,7 @@ static inline int arch_unmap_one(struct mm_struct *mm,
> >>>>>> * prototypes must be defined in the arch-specific asm/pgtable.h file.
> >>>>>> */
> >>>>>> #ifndef __HAVE_ARCH_PREPARE_TO_SWAP
> >>>>>> -static inline int arch_prepare_to_swap(struct page *page)
> >>>>>> +static inline int arch_prepare_to_swap(struct folio *folio)
> >>>>>> {
> >>>>>> return 0;
> >>>>>> }
> >>>>>> diff --git a/mm/page_io.c b/mm/page_io.c
> >>>>>> index ae2b49055e43..a9a7c236aecc 100644
> >>>>>> --- a/mm/page_io.c
> >>>>>> +++ b/mm/page_io.c
> >>>>>> @@ -189,7 +189,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
> >>>>>> * Arch code may have to preserve more data than just the page
> >>>>>> * contents, e.g. memory tags.
> >>>>>> */
> >>>>>> - ret = arch_prepare_to_swap(&folio->page);
> >>>>>> + ret = arch_prepare_to_swap(folio);
> >>>>>> if (ret) {
> >>>>>> folio_mark_dirty(folio);
> >>>>>> folio_unlock(folio);
> >>>>>> diff --git a/mm/swap_slots.c b/mm/swap_slots.c
> >>>>>> index 90973ce7881d..53abeaf1371d 100644
> >>>>>> --- a/mm/swap_slots.c
> >>>>>> +++ b/mm/swap_slots.c
> >>>>>> @@ -310,7 +310,7 @@ swp_entry_t folio_alloc_swap(struct folio *folio)
> >>>>>> entry.val = 0;
> >>>>>>
> >>>>>> if (folio_test_large(folio)) {
> >>>>>> - if (IS_ENABLED(CONFIG_THP_SWAP) && arch_thp_swp_supported())
> >>>>>> + if (IS_ENABLED(CONFIG_THP_SWAP))
> >>>>>> get_swap_pages(1, &entry, folio_nr_pages(folio));
> >>>>>> goto out;
> >>>>>> }
> >>>>>
> >>>>
> >>>> Thanks
> >>>> Barry
> >>>
>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 12/12] mm/gup: Handle hugetlb in the generic follow_page_mask code
From: Peter Xu @ 2024-03-23 2:15 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-mm, linux-kernel, linuxppc-dev, Michael Ellerman,
Christophe Leroy, Matthew Wilcox, Rik van Riel, Lorenzo Stoakes,
Axel Rasmussen, Yang Shi, John Hubbard, linux-arm-kernel,
Kirill A . Shutemov, Andrew Jones, Vlastimil Babka, Mike Rapoport,
Muchun Song, Christoph Hellwig, linux-riscv, James Houghton,
David Hildenbrand, Jason Gunthorpe, Andrea Arcangeli,
Aneesh Kumar K . V, Mike Kravetz
In-Reply-To: <Zf4mR_OxE5Ft4VJg@x1n>
On Fri, Mar 22, 2024 at 08:45:59PM -0400, Peter Xu wrote:
> On Fri, Mar 22, 2024 at 01:48:18PM -0700, Andrew Morton wrote:
> > On Thu, 21 Mar 2024 18:08:02 -0400 peterx@redhat.com wrote:
> >
> > > From: Peter Xu <peterx@redhat.com>
> > >
> > > Now follow_page() is ready to handle hugetlb pages in whatever form, and
> > > over all architectures. Switch to the generic code path.
> > >
> > > Time to retire hugetlb_follow_page_mask(), following the previous
> > > retirement of follow_hugetlb_page() in 4849807114b8.
> > >
> > > There may be a slight difference of how the loops run when processing slow
> > > GUP over a large hugetlb range on cont_pte/cont_pmd supported archs: each
> > > loop of __get_user_pages() will resolve one pgtable entry with the patch
> > > applied, rather than relying on the size of hugetlb hstate, the latter may
> > > cover multiple entries in one loop.
> > >
> > > A quick performance test on an aarch64 VM on M1 chip shows 15% degrade over
> > > a tight loop of slow gup after the path switched. That shouldn't be a
> > > problem because slow-gup should not be a hot path for GUP in general: when
> > > page is commonly present, fast-gup will already succeed, while when the
> > > page is indeed missing and require a follow up page fault, the slow gup
> > > degrade will probably buried in the fault paths anyway. It also explains
> > > why slow gup for THP used to be very slow before 57edfcfd3419 ("mm/gup:
> > > accelerate thp gup even for "pages != NULL"") lands, the latter not part of
> > > a performance analysis but a side benefit. If the performance will be a
> > > concern, we can consider handle CONT_PTE in follow_page().
> > >
> > > Before that is justified to be necessary, keep everything clean and simple.
> > >
> >
> > mm/gup.c:33:21: warning: 'follow_hugepd' declared 'static' but never defined [-Wunused-function]
> > 33 | static struct page *follow_hugepd(struct vm_area_struct *vma, hugepd_t hugepd,
> > | ^~~~~~~~~~~~~
> >
> > --- a/mm/gup.c~mm-gup-handle-hugepd-for-follow_page-fix
> > +++ a/mm/gup.c
> > @@ -30,10 +30,12 @@ struct follow_page_context {
> > unsigned int page_mask;
> > };
> >
> > +#ifdef CONFIG_HAVE_FAST_GUP
> > static struct page *follow_hugepd(struct vm_area_struct *vma, hugepd_t hugepd,
> > unsigned long addr, unsigned int pdshift,
> > unsigned int flags,
> > struct follow_page_context *ctx);
> > +#endif
> >
> > static inline void sanity_check_pinned_pages(struct page **pages,
> > unsigned long npages)
> > _
> >
> >
> > This looks inelegant.
> >
> > That's two build issues so far. Please be more expansive in the
> > Kconfig variations when testing. Especially when mucking with pgtable
> > macros.
>
> Andrew,
>
> Apologies for that, and also for a slightly late response. Yeah it's time
> I'll need my set of things to do serious build tests, and I'll at least
> start to cover a few error prone configs/archs in with that.
>
> I was trying to rely on the build bot in many of previous such cases, as
> that was quite useful to me to cover many build issues without investing my
> own test setups, but I think for some reason it retired and stopped working
> for a while. Maybe I shouldn't have relied on it at all.
>
> For this specific issue, I'm not sure if CONFIG_HAVE_FAST_GUP is proper?
> As follow_hugepd() is used in slow gup not fast. So maybe we can put that
> under CONFIG_MMU below that code (and I think we can drop "static" too as I
> don't think it's anything useful). My version of fixup attached at the end
the static is useful; below patch did pass on m68k but won't on
x86.. ignore that please.
> of email, and I verified it on m68k build.
>
> I do plan to post a small fixup series to fix these issues (so far it may
> contain 1 formal patch to touch up vmstat_item_print_in_thp, and 2 fixups
> where I'll mark the subject with "fixup!" properly). Either you can pick
> up below or you can wait for my small patchset, should be there either
> today or tomorrow.
I changed plan here too; I found more users of HPAGE_PMD_NR assuming it's
defined even if !CONFIG_MMU. That's weird, as CONFIG_MMU doesn't even
define PMD_SHIFT... To fix this I decided to use the old trick on using
BUILD_BUG() like it used to work before; frankly I don't know how that
didn't throw warnings, but i'll make sure it passes all known builds (ps: I
still haven't got my build harness ready, so that will still be limited but
should solve known issues).
In short: please wait for my fixup series. Thanks.
>
> Thanks,
>
> ===8<===
> diff --git a/mm/gup.c b/mm/gup.c
> index 4cd349390477..a2ed8203495a 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -30,11 +30,6 @@ struct follow_page_context {
> unsigned int page_mask;
> };
>
> -static struct page *follow_hugepd(struct vm_area_struct *vma, hugepd_t hugepd,
> - unsigned long addr, unsigned int pdshift,
> - unsigned int flags,
> - struct follow_page_context *ctx);
> -
> static inline void sanity_check_pinned_pages(struct page **pages,
> unsigned long npages)
> {
> @@ -505,6 +500,12 @@ static inline void mm_set_has_pinned_flag(unsigned long *mm_flags)
> }
>
> #ifdef CONFIG_MMU
> +
> +struct page *follow_hugepd(struct vm_area_struct *vma, hugepd_t hugepd,
> + unsigned long addr, unsigned int pdshift,
> + unsigned int flags,
> + struct follow_page_context *ctx);
> +
> static struct page *no_page_table(struct vm_area_struct *vma,
> unsigned int flags, unsigned long address)
> {
> ===8<===
>
> --
> Peter Xu
--
Peter Xu
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/2] KVM: arm64: Allow only the specified FF-A calls to be forwarded to TZ
From: Oliver Upton @ 2024-03-23 2:07 UTC (permalink / raw)
To: Sebastian Ene
Cc: catalin.marinas, james.morse, jean-philippe, maz, qperret,
qwandor, suzuki.poulose, tabba, will, yuzenghui, kvmarm,
linux-arm-kernel, linux-kernel, kernel-team
In-Reply-To: <20240322124303.309423-2-sebastianene@google.com>
On Fri, Mar 22, 2024 at 12:43:03PM +0000, Sebastian Ene wrote:
> The previous logic used a deny list to filter the FF-A calls. Because of
> this, some of the calls escaped the check and they were forwarded by
> default to Trustzone. (eg. FFA_MSG_SEND_DIRECT_REQ was denied but the 64
> bit version of the call was not).
> Modify the logic to use an allowlist and allow only the calls specified in
> the filter function to be proxied to TZ from the hypervisor.
I had discussed this with Will back when the feature was upstreamed and
he said there's a lot of off-label calls that necessitate a denylist
implementation. Has anything changed to give us confidence that we can
be restrictive, at least on the FF-A range?
--
Thanks,
Oliver
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [WIP 0/3] Memory model and atomic API in Rust
From: Kent Overstreet @ 2024-03-23 2:07 UTC (permalink / raw)
To: Linus Torvalds
Cc: Boqun Feng, rust-for-linux, linux-kernel, linux-arch, llvm,
Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Alan Stern, Andrea Parri, Will Deacon, Peter Zijlstra,
Nicholas Piggin, David Howells, Jade Alglave, Luc Maranget,
Paul E. McKenney, Akira Yokosawa, Daniel Lustig, Joel Fernandes,
Nathan Chancellor, Nick Desaulniers, kent.overstreet,
Greg Kroah-Hartman, elver, Mark Rutland, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Catalin Marinas, linux-arm-kernel, linux-fsdevel
In-Reply-To: <CAHk-=whkQk=zq5XiMcaU3xj4v69+jyoP-y6Sywhq-TvxSSvfEA@mail.gmail.com>
On Fri, Mar 22, 2024 at 05:36:00PM -0700, Linus Torvalds wrote:
> On Fri, 22 Mar 2024 at 17:21, Kent Overstreet <kent.overstreet@linux.dev> wrote:
> >
> > Besides that there's cross arch support to think about - it's hard to
> > imagine us ever ditching our own atomics.
>
> Well, that's one of the advantages of using compiler builtins -
> projects that do want cross-architecture support, but that aren't
> actually maintaining their _own_ architecture support.
>
> So I very much see the lure of compiler support for that kind of
> situation - to write portable code without having to know or care
> about architecture details.
>
> This is one reason I think the kernel is kind of odd and special -
> because in the kernel, we obviously very fundamentally have to care
> about the architecture details _anyway_, so then having the
> architecture also define things like atomics is just a pretty small
> (and relatively straightforward) detail.
>
> The same argument goes for compiler builtins vs inline asm. In the
> kernel, we have to have people who are intimately familiar with the
> architecture _anyway_, so inline asms and architecture-specific header
> files aren't some big pain-point: they'd be needed _anyway_.
>
> But in some random user level program, where all you want is an
> efficient way to do "find first bit"? Then using a compiler intrinsic
> makes a lot more sense.
We've got a whole spectrum of kernel code though, and a lot of it is
code that - honestly, we'd be better off if it wasn't specific to the
kernel.
rhashtable comes to mind; it's a fully generic, excellent at what it
does, but it's had a number of annoyingly subtle bugs and sharp edges
over the years that are really just a result of it not having enough
users.
So I see some real value in regularizing things.
> > I was thinking about something more incremental - just an optional mode
> > where our atomics were C atomics underneath. It'd probably give the
> > compiler people a much more effective way to test their stuff than
> > anything they have now.
>
> I suspect it might be painful, and some compiler people would throw
> their hands up in horror, because the C++ atomics model is based
> fairly solidly on atomic types, and the kernel memory model is much
> more fluid.
>
> Boqun already mentioned the "mixing access sizes", which is actually
> quite fundamental in the kernel, where we play lots of games with that
> (typically around locking, where you find patterns line unlock writing
> a zero to a single byte, even though the whole lock data structure is
> a word). And sometimes the access size games are very explicit (eg
> lib/lockref.c).
I don't think mixing access sizes should be a real barrier. On the read
side we can obviously do that with a helper; the write side needs
compiler help, but "writing just a byte out of a word" is no different
from a compiler POV that "write a single bit", and we can already mix
atomic_or() with atomic_add(), with both C atomics and LKMM atomics.
> But it actually goes deeper than that. While we do have "atomic_t" etc
> for arithmetic atomics, and that probably would map fairly well to C++
> atomics, in other cases we simply base our atomics not on _types_, but
> on code.
>
> IOW, we do things like "cmpxchg()", and the target of that atomic
> access is just a regular data structure field.
Well, some of that's historical cruft; cmpxchg() and atomic_cmpxchg()
have different orderings, and we can specify that more directly now.
But we definitely need the ability to cmpxchg() any struct of a size the
machine supports atomic access to. Rust should be able to manage that
more easily than C/C++ though - they've got a type system that can
sanely represent that.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v2 2/2] media: i2c: Add GC05A2 image sensor driver
From: Zhi Mao @ 2024-03-23 1:47 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20240323014751.4989-1-zhi.mao@mediatek.com>
Add a V4L2 sub-device driver for Galaxycore GC05A2 image sensor.
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc05a2.c | 1387 ++++++++++++++++++++++++++++++++++++
3 files changed, 1398 insertions(+)
create mode 100644 drivers/media/i2c/gc05a2.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 56f276b920ab..97993bf160f9 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -70,6 +70,16 @@ config VIDEO_GC0308
To compile this driver as a module, choose M here: the
module will be called gc0308.
+config VIDEO_GC05A2
+ tristate "GalaxyCore gc05a2 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for the GalaxyCore gc05a2
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gc05a2.
+
config VIDEO_GC2145
select V4L2_CCI_I2C
tristate "GalaxyCore GC2145 sensor support"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index dfbe6448b549..8ed6faf0f854 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
obj-$(CONFIG_VIDEO_GC0308) += gc0308.o
+obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o
obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c
new file mode 100644
index 000000000000..22df177c362f
--- /dev/null
+++ b/drivers/media/i2c/gc05a2.c
@@ -0,0 +1,1387 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for GalaxyCore gc05a2 image sensor
+ *
+ * Copyright 2024 MediaTek
+ *
+ * Zhi Mao <zhi.mao@mediatek.com>
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define GC05A2_REG_TEST_PATTERN_EN CCI_REG8(0x008c)
+#define GC05A2_REG_TEST_PATTERN_IDX CCI_REG8(0x008d)
+#define GC05A2_TEST_PATTERN_EN 0x01
+
+#define GC05A2_STREAMING_REG CCI_REG8(0x0100)
+
+#define GC05A2_FLIP_REG CCI_REG8(0x0101)
+#define GC05A2_FLIP_H_MASK BIT(0)
+#define GC05A2_FLIP_V_MASK BIT(1)
+
+#define GC05A2_EXP_REG CCI_REG16(0x0202)
+#define GC05A2_EXP_MARGIN 16
+#define GC05A2_EXP_MIN 4
+#define GC05A2_EXP_STEP 1
+
+#define GC05A2_AGAIN_REG CCI_REG16(0x0204)
+#define GC05A2_AGAIN_MIN 1024
+#define GC05A2_AGAIN_MAX (1024 * 16)
+#define GC05A2_AGAIN_STEP 1
+
+#define GC05A2_FRAME_LENGTH_REG CCI_REG16(0x0340)
+#define GC05A2_VTS_MAX 0xffff
+
+#define GC05A2_REG_CHIP_ID CCI_REG16(0x03f0)
+#define GC05A2_CHIP_ID 0x05a2
+
+#define GC05A2_NATIVE_WIDTH 2592
+#define GC05A2_NATIVE_HEIGHT 1944
+
+#define GC05A2_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ)
+#define GC05A2_MBUS_CODE MEDIA_BUS_FMT_SGRBG10_1X10
+#define GC05A2_DATA_LANES 2
+#define GC05A2_RGB_DEPTH 10
+#define GC05A2_SLEEP_US (2 * USEC_PER_MSEC)
+
+static const char *const gc05a2_test_pattern_menu[] = {
+ "No Pattern", "Fade_to_gray_Color Bar", "Color Bar",
+ "PN9", "Horizental_gradient", "Checkboard Pattern",
+ "Slant", "Resolution", "Solid Black",
+ "Solid White",
+};
+
+static const s64 gc05a2_link_freq_menu_items[] = {
+ (448 * HZ_PER_MHZ),
+ (224 * HZ_PER_MHZ),
+};
+
+static const char *const gc05a2_supply_name[] = {
+ "avdd",
+ "dvdd",
+ "dovdd",
+};
+
+struct gc05a2 {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct clk *xclk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(gc05a2_supply_name)];
+ struct gpio_desc *reset_gpio;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ struct regmap *regmap;
+ unsigned long link_freq_bitmap;
+ const struct gc05a2_mode *cur_mode;
+};
+
+struct gc05a2_reg_list {
+ u32 num_of_regs;
+ const struct cci_reg_sequence *regs;
+};
+
+static const struct cci_reg_sequence mode_2592x1944[] = {
+ /* system */
+ { CCI_REG8(0x0135), 0x01 },
+
+ /* pre_setting */
+ { CCI_REG8(0x0084), 0x21 },
+ { CCI_REG8(0x0d05), 0xcc },
+ { CCI_REG8(0x0218), 0x00 },
+ { CCI_REG8(0x005e), 0x48 },
+ { CCI_REG8(0x0d06), 0x01 },
+ { CCI_REG8(0x0007), 0x16 },
+ { CCI_REG8(0x0101), 0x00 },
+
+ /* analog */
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x28 },
+ { CCI_REG8(0x0220), 0x07 },
+ { CCI_REG8(0x0221), 0xd0 },
+ { CCI_REG8(0x0202), 0x07 },
+ { CCI_REG8(0x0203), 0x32 },
+ { CCI_REG8(0x0340), 0x07 },
+ { CCI_REG8(0x0341), 0xf0 },
+ { CCI_REG8(0x0219), 0x00 },
+ { CCI_REG8(0x0346), 0x00 },
+ { CCI_REG8(0x0347), 0x04 },
+ { CCI_REG8(0x0d14), 0x00 },
+ { CCI_REG8(0x0d13), 0x05 },
+ { CCI_REG8(0x0d16), 0x05 },
+ { CCI_REG8(0x0d15), 0x1d },
+ { CCI_REG8(0x00c0), 0x0a },
+ { CCI_REG8(0x00c1), 0x30 },
+ { CCI_REG8(0x034a), 0x07 },
+ { CCI_REG8(0x034b), 0xa8 },
+ { CCI_REG8(0x0e0a), 0x00 },
+ { CCI_REG8(0x0e0b), 0x00 },
+ { CCI_REG8(0x0e0e), 0x03 },
+ { CCI_REG8(0x0e0f), 0x00 },
+ { CCI_REG8(0x0e06), 0x0a },
+ { CCI_REG8(0x0e23), 0x15 },
+ { CCI_REG8(0x0e24), 0x15 },
+ { CCI_REG8(0x0e2a), 0x10 },
+ { CCI_REG8(0x0e2b), 0x10 },
+ { CCI_REG8(0x0e17), 0x49 },
+ { CCI_REG8(0x0e1b), 0x1c },
+ { CCI_REG8(0x0e3a), 0x36 },
+ { CCI_REG8(0x0d11), 0x84 },
+ { CCI_REG8(0x0e52), 0x14 },
+ { CCI_REG8(0x000b), 0x10 },
+ { CCI_REG8(0x0008), 0x08 },
+ { CCI_REG8(0x0223), 0x17 },
+ { CCI_REG8(0x0d27), 0x39 },
+ { CCI_REG8(0x0d22), 0x00 },
+ { CCI_REG8(0x03f6), 0x0d },
+ { CCI_REG8(0x0d04), 0x07 },
+ { CCI_REG8(0x03f3), 0x72 },
+ { CCI_REG8(0x03f4), 0xb8 },
+ { CCI_REG8(0x03f5), 0xbc },
+ { CCI_REG8(0x0d02), 0x73 },
+
+ /* auto load start */
+ { CCI_REG8(0x00cb), 0x00 },
+
+ /* OUT 2592*1944 */
+ { CCI_REG8(0x0350), 0x01 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x08 },
+ { CCI_REG8(0x034c), 0x0a },
+ { CCI_REG8(0x034d), 0x20 },
+ { CCI_REG8(0x021f), 0x14 },
+
+ /* MIPI */
+ { CCI_REG8(0x0107), 0x05 },
+ { CCI_REG8(0x0117), 0x01 },
+ { CCI_REG8(0x0d81), 0x00 },
+ { CCI_REG8(0x0d84), 0x0c },
+ { CCI_REG8(0x0d85), 0xa8 },
+ { CCI_REG8(0x0d86), 0x06 },
+ { CCI_REG8(0x0d87), 0x55 },
+ { CCI_REG8(0x0db3), 0x06 },
+ { CCI_REG8(0x0db4), 0x08 },
+ { CCI_REG8(0x0db5), 0x1e },
+ { CCI_REG8(0x0db6), 0x02 },
+ { CCI_REG8(0x0db8), 0x12 },
+ { CCI_REG8(0x0db9), 0x0a },
+ { CCI_REG8(0x0d93), 0x06 },
+ { CCI_REG8(0x0d94), 0x09 },
+ { CCI_REG8(0x0d95), 0x0d },
+ { CCI_REG8(0x0d99), 0x0b },
+ { CCI_REG8(0x0084), 0x01 },
+
+ /* OUT */
+ { CCI_REG8(0x0110), 0x01 },
+};
+
+static const struct cci_reg_sequence mode_1280x720[] = {
+ /* system */
+ { CCI_REG8(0x0135), 0x05 },
+
+ /*pre_setting*/
+ { CCI_REG8(0x0084), 0x21 },
+ { CCI_REG8(0x0d05), 0xcc },
+ { CCI_REG8(0x0218), 0x80 },
+ { CCI_REG8(0x005e), 0x49 },
+ { CCI_REG8(0x0d06), 0x81 },
+ { CCI_REG8(0x0007), 0x16 },
+ { CCI_REG8(0x0101), 0x00 },
+
+ /* analog */
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x10 },
+ { CCI_REG8(0x0220), 0x07 },
+ { CCI_REG8(0x0221), 0xd0 },
+ { CCI_REG8(0x0202), 0x03 },
+ { CCI_REG8(0x0203), 0x32 },
+ { CCI_REG8(0x0340), 0x04 },
+ { CCI_REG8(0x0341), 0x08 },
+ { CCI_REG8(0x0219), 0x00 },
+ { CCI_REG8(0x0346), 0x01 },
+ { CCI_REG8(0x0347), 0x00 },
+ { CCI_REG8(0x0d14), 0x00 },
+ { CCI_REG8(0x0d13), 0x05 },
+ { CCI_REG8(0x0d16), 0x05 },
+ { CCI_REG8(0x0d15), 0x1d },
+ { CCI_REG8(0x00c0), 0x0a },
+ { CCI_REG8(0x00c1), 0x30 },
+ { CCI_REG8(0x034a), 0x05 },
+ { CCI_REG8(0x034b), 0xb0 },
+ { CCI_REG8(0x0e0a), 0x00 },
+ { CCI_REG8(0x0e0b), 0x00 },
+ { CCI_REG8(0x0e0e), 0x03 },
+ { CCI_REG8(0x0e0f), 0x00 },
+ { CCI_REG8(0x0e06), 0x0a },
+ { CCI_REG8(0x0e23), 0x15 },
+ { CCI_REG8(0x0e24), 0x15 },
+ { CCI_REG8(0x0e2a), 0x10 },
+ { CCI_REG8(0x0e2b), 0x10 },
+ { CCI_REG8(0x0e17), 0x49 },
+ { CCI_REG8(0x0e1b), 0x1c },
+ { CCI_REG8(0x0e3a), 0x36 },
+ { CCI_REG8(0x0d11), 0x84 },
+ { CCI_REG8(0x0e52), 0x14 },
+ { CCI_REG8(0x000b), 0x0e },
+ { CCI_REG8(0x0008), 0x03 },
+ { CCI_REG8(0x0223), 0x16 },
+ { CCI_REG8(0x0d27), 0x39 },
+ { CCI_REG8(0x0d22), 0x00 },
+ { CCI_REG8(0x03f6), 0x0d },
+ { CCI_REG8(0x0d04), 0x07 },
+ { CCI_REG8(0x03f3), 0x72 },
+ { CCI_REG8(0x03f4), 0xb8 },
+ { CCI_REG8(0x03f5), 0xbc },
+ { CCI_REG8(0x0d02), 0x73 },
+
+ /* auto load start */
+ { CCI_REG8(0x00cb), 0xfc },
+
+ /* OUT 1280x720 */
+ { CCI_REG8(0x0350), 0x01 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x0c },
+ { CCI_REG8(0x034c), 0x05 },
+ { CCI_REG8(0x034d), 0x00 },
+ { CCI_REG8(0x021f), 0x14 },
+
+ /* MIPI */
+ { CCI_REG8(0x0107), 0x05 },
+ { CCI_REG8(0x0117), 0x01 },
+ { CCI_REG8(0x0d81), 0x00 },
+ { CCI_REG8(0x0d84), 0x06 },
+ { CCI_REG8(0x0d85), 0x40 },
+ { CCI_REG8(0x0d86), 0x03 },
+ { CCI_REG8(0x0d87), 0x21 },
+ { CCI_REG8(0x0db3), 0x03 },
+ { CCI_REG8(0x0db4), 0x04 },
+ { CCI_REG8(0x0db5), 0x0d },
+ { CCI_REG8(0x0db6), 0x01 },
+ { CCI_REG8(0x0db8), 0x04 },
+ { CCI_REG8(0x0db9), 0x06 },
+ { CCI_REG8(0x0d93), 0x03 },
+ { CCI_REG8(0x0d94), 0x04 },
+ { CCI_REG8(0x0d95), 0x05 },
+ { CCI_REG8(0x0d99), 0x06 },
+ { CCI_REG8(0x0084), 0x01 },
+
+ /* OUT */
+ { CCI_REG8(0x0110), 0x01 },
+};
+
+static const struct cci_reg_sequence mode_table_common[] = {
+ { GC05A2_STREAMING_REG, 0x00 },
+ /* system */
+ { CCI_REG8(0x0315), 0xd4 },
+ { CCI_REG8(0x0d06), 0x01 },
+ { CCI_REG8(0x0a70), 0x80 },
+ { CCI_REG8(0x031a), 0x00 },
+ { CCI_REG8(0x0314), 0x00 },
+ { CCI_REG8(0x0130), 0x08 },
+ { CCI_REG8(0x0132), 0x01 },
+ { CCI_REG8(0x0136), 0x38 },
+ { CCI_REG8(0x0137), 0x03 },
+ { CCI_REG8(0x0134), 0x5b },
+ { CCI_REG8(0x031c), 0xe0 },
+ { CCI_REG8(0x0d82), 0x14 },
+ { CCI_REG8(0x0dd1), 0x56 },
+
+ /* gate_mode */
+ { CCI_REG8(0x0af4), 0x01 },
+ { CCI_REG8(0x0002), 0x10 },
+ { CCI_REG8(0x00c3), 0x34 },
+
+ /* auto load start */
+ { CCI_REG8(0x00c4), 0x00 },
+ { CCI_REG8(0x00c5), 0x01 },
+ { CCI_REG8(0x0af6), 0x00 },
+ { CCI_REG8(0x0ba0), 0x17 },
+ { CCI_REG8(0x0ba1), 0x00 },
+ { CCI_REG8(0x0ba2), 0x00 },
+ { CCI_REG8(0x0ba3), 0x00 },
+ { CCI_REG8(0x0ba4), 0x03 },
+ { CCI_REG8(0x0ba5), 0x00 },
+ { CCI_REG8(0x0ba6), 0x00 },
+ { CCI_REG8(0x0ba7), 0x00 },
+ { CCI_REG8(0x0ba8), 0x40 },
+ { CCI_REG8(0x0ba9), 0x00 },
+ { CCI_REG8(0x0baa), 0x00 },
+ { CCI_REG8(0x0bab), 0x00 },
+ { CCI_REG8(0x0bac), 0x40 },
+ { CCI_REG8(0x0bad), 0x00 },
+ { CCI_REG8(0x0bae), 0x00 },
+ { CCI_REG8(0x0baf), 0x00 },
+ { CCI_REG8(0x0bb0), 0x02 },
+ { CCI_REG8(0x0bb1), 0x00 },
+ { CCI_REG8(0x0bb2), 0x00 },
+ { CCI_REG8(0x0bb3), 0x00 },
+ { CCI_REG8(0x0bb8), 0x02 },
+ { CCI_REG8(0x0bb9), 0x00 },
+ { CCI_REG8(0x0bba), 0x00 },
+ { CCI_REG8(0x0bbb), 0x00 },
+ { CCI_REG8(0x0a70), 0x80 },
+ { CCI_REG8(0x0a71), 0x00 },
+ { CCI_REG8(0x0a72), 0x00 },
+ { CCI_REG8(0x0a66), 0x00 },
+ { CCI_REG8(0x0a67), 0x80 },
+ { CCI_REG8(0x0a4d), 0x4e },
+ { CCI_REG8(0x0a50), 0x00 },
+ { CCI_REG8(0x0a4f), 0x0c },
+ { CCI_REG8(0x0a66), 0x00 },
+ { CCI_REG8(0x00ca), 0x00 },
+ { CCI_REG8(0x00cc), 0x00 },
+ { CCI_REG8(0x00cd), 0x00 },
+ { CCI_REG8(0x0aa1), 0x00 },
+ { CCI_REG8(0x0aa2), 0xe0 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x40 },
+ { CCI_REG8(0x0a90), 0x03 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+
+ /* standby */
+ { CCI_REG8(0x0af6), 0x20 },
+ { CCI_REG8(0x0b00), 0x91 },
+ { CCI_REG8(0x0b01), 0x17 },
+ { CCI_REG8(0x0b02), 0x01 },
+ { CCI_REG8(0x0b03), 0x00 },
+ { CCI_REG8(0x0b04), 0x01 },
+ { CCI_REG8(0x0b05), 0x17 },
+ { CCI_REG8(0x0b06), 0x01 },
+ { CCI_REG8(0x0b07), 0x00 },
+ { CCI_REG8(0x0ae9), 0x01 },
+ { CCI_REG8(0x0aea), 0x02 },
+ { CCI_REG8(0x0ae8), 0x53 },
+ { CCI_REG8(0x0ae8), 0x43 },
+
+ /* gain_partition */
+ { CCI_REG8(0x0af6), 0x30 },
+ { CCI_REG8(0x0b00), 0x08 },
+ { CCI_REG8(0x0b01), 0x0f },
+ { CCI_REG8(0x0b02), 0x00 },
+ { CCI_REG8(0x0b04), 0x1c },
+ { CCI_REG8(0x0b05), 0x24 },
+ { CCI_REG8(0x0b06), 0x00 },
+ { CCI_REG8(0x0b08), 0x30 },
+ { CCI_REG8(0x0b09), 0x40 },
+ { CCI_REG8(0x0b0a), 0x00 },
+ { CCI_REG8(0x0b0c), 0x0e },
+ { CCI_REG8(0x0b0d), 0x2a },
+ { CCI_REG8(0x0b0e), 0x00 },
+ { CCI_REG8(0x0b10), 0x0e },
+ { CCI_REG8(0x0b11), 0x2b },
+ { CCI_REG8(0x0b12), 0x00 },
+ { CCI_REG8(0x0b14), 0x0e },
+ { CCI_REG8(0x0b15), 0x23 },
+ { CCI_REG8(0x0b16), 0x00 },
+ { CCI_REG8(0x0b18), 0x0e },
+ { CCI_REG8(0x0b19), 0x24 },
+ { CCI_REG8(0x0b1a), 0x00 },
+ { CCI_REG8(0x0b1c), 0x0c },
+ { CCI_REG8(0x0b1d), 0x0c },
+ { CCI_REG8(0x0b1e), 0x00 },
+ { CCI_REG8(0x0b20), 0x03 },
+ { CCI_REG8(0x0b21), 0x03 },
+ { CCI_REG8(0x0b22), 0x00 },
+ { CCI_REG8(0x0b24), 0x0e },
+ { CCI_REG8(0x0b25), 0x0e },
+ { CCI_REG8(0x0b26), 0x00 },
+ { CCI_REG8(0x0b28), 0x03 },
+ { CCI_REG8(0x0b29), 0x03 },
+ { CCI_REG8(0x0b2a), 0x00 },
+ { CCI_REG8(0x0b2c), 0x12 },
+ { CCI_REG8(0x0b2d), 0x12 },
+ { CCI_REG8(0x0b2e), 0x00 },
+ { CCI_REG8(0x0b30), 0x08 },
+ { CCI_REG8(0x0b31), 0x08 },
+ { CCI_REG8(0x0b32), 0x00 },
+ { CCI_REG8(0x0b34), 0x14 },
+ { CCI_REG8(0x0b35), 0x14 },
+ { CCI_REG8(0x0b36), 0x00 },
+ { CCI_REG8(0x0b38), 0x10 },
+ { CCI_REG8(0x0b39), 0x10 },
+ { CCI_REG8(0x0b3a), 0x00 },
+ { CCI_REG8(0x0b3c), 0x16 },
+ { CCI_REG8(0x0b3d), 0x16 },
+ { CCI_REG8(0x0b3e), 0x00 },
+ { CCI_REG8(0x0b40), 0x10 },
+ { CCI_REG8(0x0b41), 0x10 },
+ { CCI_REG8(0x0b42), 0x00 },
+ { CCI_REG8(0x0b44), 0x19 },
+ { CCI_REG8(0x0b45), 0x19 },
+ { CCI_REG8(0x0b46), 0x00 },
+ { CCI_REG8(0x0b48), 0x16 },
+ { CCI_REG8(0x0b49), 0x16 },
+ { CCI_REG8(0x0b4a), 0x00 },
+ { CCI_REG8(0x0b4c), 0x19 },
+ { CCI_REG8(0x0b4d), 0x19 },
+ { CCI_REG8(0x0b4e), 0x00 },
+ { CCI_REG8(0x0b50), 0x16 },
+ { CCI_REG8(0x0b51), 0x16 },
+ { CCI_REG8(0x0b52), 0x00 },
+ { CCI_REG8(0x0b80), 0x01 },
+ { CCI_REG8(0x0b81), 0x00 },
+ { CCI_REG8(0x0b82), 0x00 },
+ { CCI_REG8(0x0b84), 0x00 },
+ { CCI_REG8(0x0b85), 0x00 },
+ { CCI_REG8(0x0b86), 0x00 },
+ { CCI_REG8(0x0b88), 0x01 },
+ { CCI_REG8(0x0b89), 0x6a },
+ { CCI_REG8(0x0b8a), 0x00 },
+ { CCI_REG8(0x0b8c), 0x00 },
+ { CCI_REG8(0x0b8d), 0x01 },
+ { CCI_REG8(0x0b8e), 0x00 },
+ { CCI_REG8(0x0b90), 0x01 },
+ { CCI_REG8(0x0b91), 0xf6 },
+ { CCI_REG8(0x0b92), 0x00 },
+ { CCI_REG8(0x0b94), 0x00 },
+ { CCI_REG8(0x0b95), 0x02 },
+ { CCI_REG8(0x0b96), 0x00 },
+ { CCI_REG8(0x0b98), 0x02 },
+ { CCI_REG8(0x0b99), 0xc4 },
+ { CCI_REG8(0x0b9a), 0x00 },
+ { CCI_REG8(0x0b9c), 0x00 },
+ { CCI_REG8(0x0b9d), 0x03 },
+ { CCI_REG8(0x0b9e), 0x00 },
+ { CCI_REG8(0x0ba0), 0x03 },
+ { CCI_REG8(0x0ba1), 0xd8 },
+ { CCI_REG8(0x0ba2), 0x00 },
+ { CCI_REG8(0x0ba4), 0x00 },
+ { CCI_REG8(0x0ba5), 0x04 },
+ { CCI_REG8(0x0ba6), 0x00 },
+ { CCI_REG8(0x0ba8), 0x05 },
+ { CCI_REG8(0x0ba9), 0x4d },
+ { CCI_REG8(0x0baa), 0x00 },
+ { CCI_REG8(0x0bac), 0x00 },
+ { CCI_REG8(0x0bad), 0x05 },
+ { CCI_REG8(0x0bae), 0x00 },
+ { CCI_REG8(0x0bb0), 0x07 },
+ { CCI_REG8(0x0bb1), 0x3e },
+ { CCI_REG8(0x0bb2), 0x00 },
+ { CCI_REG8(0x0bb4), 0x00 },
+ { CCI_REG8(0x0bb5), 0x06 },
+ { CCI_REG8(0x0bb6), 0x00 },
+ { CCI_REG8(0x0bb8), 0x0a },
+ { CCI_REG8(0x0bb9), 0x1a },
+ { CCI_REG8(0x0bba), 0x00 },
+ { CCI_REG8(0x0bbc), 0x09 },
+ { CCI_REG8(0x0bbd), 0x36 },
+ { CCI_REG8(0x0bbe), 0x00 },
+ { CCI_REG8(0x0bc0), 0x0e },
+ { CCI_REG8(0x0bc1), 0x66 },
+ { CCI_REG8(0x0bc2), 0x00 },
+ { CCI_REG8(0x0bc4), 0x10 },
+ { CCI_REG8(0x0bc5), 0x06 },
+ { CCI_REG8(0x0bc6), 0x00 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c3), 0x74 },
+ { CCI_REG8(0x02c5), 0x09 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c5), 0x09 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c5), 0x09 },
+
+ /* auto load CH_GAIN */
+ { CCI_REG8(0x0aa1), 0x15 },
+ { CCI_REG8(0x0aa2), 0x50 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x09 },
+ { CCI_REG8(0x0a90), 0x25 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+
+ /* ISP */
+ { CCI_REG8(0x0050), 0x00 },
+ { CCI_REG8(0x0089), 0x83 },
+ { CCI_REG8(0x005a), 0x40 },
+ { CCI_REG8(0x00c3), 0x35 },
+ { CCI_REG8(0x00c4), 0x80 },
+ { CCI_REG8(0x0080), 0x10 },
+ { CCI_REG8(0x0040), 0x12 },
+ { CCI_REG8(0x0053), 0x0a },
+ { CCI_REG8(0x0054), 0x44 },
+ { CCI_REG8(0x0055), 0x32 },
+ { CCI_REG8(0x0058), 0x89 },
+ { CCI_REG8(0x004a), 0x03 },
+ { CCI_REG8(0x0048), 0xf0 },
+ { CCI_REG8(0x0049), 0x0f },
+ { CCI_REG8(0x0041), 0x20 },
+ { CCI_REG8(0x0043), 0x0a },
+ { CCI_REG8(0x009d), 0x08 },
+ { CCI_REG8(0x0236), 0x40 },
+
+ /* gain */
+ { CCI_REG8(0x0204), 0x04 },
+ { CCI_REG8(0x0205), 0x00 },
+ { CCI_REG8(0x02b3), 0x00 },
+ { CCI_REG8(0x02b4), 0x00 },
+ { CCI_REG8(0x009e), 0x01 },
+ { CCI_REG8(0x009f), 0x94 },
+
+ /* auto load REG */
+ { CCI_REG8(0x0aa1), 0x10 },
+ { CCI_REG8(0x0aa2), 0xf8 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x1f },
+ { CCI_REG8(0x0a90), 0x11 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0a90), 0x00 },
+ { CCI_REG8(0x0a70), 0x00 },
+ { CCI_REG8(0x0a67), 0x00 },
+ { CCI_REG8(0x0af4), 0x29 },
+
+ /* DPHY */
+ { CCI_REG8(0x0d80), 0x07 },
+ { CCI_REG8(0x0dd3), 0x18 },
+
+ /* CISCTL_Reset */
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x30 },
+ { CCI_REG8(0x0d17), 0x06 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0d17), 0x00 },
+ { CCI_REG8(0x031c), 0x93 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x30 },
+ { CCI_REG8(0x0d17), 0x06 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0d17), 0x00 },
+ { CCI_REG8(0x031c), 0x93 },
+};
+
+struct gc05a2_mode {
+ u32 width;
+ u32 height;
+ const struct gc05a2_reg_list reg_list;
+
+ u32 hts; /* Horizontal timining size */
+ u32 vts_def; /* Default vertical timining size */
+ u32 vts_min; /* Min vertical timining size */
+};
+
+/* Declare modes in order, from biggest to smallest height. */
+static const struct gc05a2_mode gc05a2_modes[] = {
+ {
+ /* 2592*1944@30fps */
+ .width = GC05A2_NATIVE_WIDTH,
+ .height = GC05A2_NATIVE_HEIGHT,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944),
+ .regs = mode_2592x1944,
+ },
+ .hts = 3664,
+ .vts_def = 2032,
+ .vts_min = 2032,
+ },
+ {
+ /* 1280*720@60fps */
+ .width = 1280,
+ .height = 720,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1280x720),
+ .regs = mode_1280x720,
+ },
+ .hts = 3616,
+ .vts_def = 1032,
+ .vts_min = 1032,
+ },
+};
+
+static inline struct gc05a2 *to_gc05a2(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct gc05a2, sd);
+}
+
+static int gc05a2_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(gc05a2->xclk);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+ dev_err(gc05a2->dev, "clk prepare enable failed\n");
+ return ret;
+ }
+
+ fsleep(GC05A2_SLEEP_US);
+
+ gpiod_set_value_cansleep(gc05a2->reset_gpio, 0);
+ fsleep(GC05A2_SLEEP_US);
+
+ return 0;
+}
+
+static int gc05a2_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+
+ clk_disable_unprepare(gc05a2->xclk);
+ gpiod_set_value_cansleep(gc05a2->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+
+ return 0;
+}
+
+static int gc05a2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = GC05A2_MBUS_CODE;
+
+ return 0;
+}
+
+static int gc05a2_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->code != GC05A2_MBUS_CODE)
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(gc05a2_modes))
+ return -EINVAL;
+
+ fse->min_width = gc05a2_modes[fse->index].width;
+ fse->max_width = gc05a2_modes[fse->index].width;
+ fse->min_height = gc05a2_modes[fse->index].height;
+ fse->max_height = gc05a2_modes[fse->index].height;
+
+ return 0;
+}
+
+static int gc05a2_update_cur_mode_controls(struct gc05a2 *gc05a2,
+ const struct gc05a2_mode *mode)
+{
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ret = __v4l2_ctrl_modify_range(gc05a2->vblank,
+ mode->vts_min - mode->height,
+ GC05A2_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+ if (ret) {
+ dev_err(gc05a2->dev, "VB ctrl range update failed\n");
+ return ret;
+ }
+
+ h_blank = mode->hts - mode->width;
+ ret = __v4l2_ctrl_modify_range(gc05a2->hblank, h_blank, h_blank, 1,
+ h_blank);
+ if (ret) {
+ dev_err(gc05a2->dev, "HB ctrl range update failed\n");
+ return ret;
+ }
+
+ exposure_max = mode->vts_def - GC05A2_EXP_MARGIN;
+ ret = __v4l2_ctrl_modify_range(gc05a2->exposure, GC05A2_EXP_MIN,
+ exposure_max, GC05A2_EXP_STEP,
+ exposure_max);
+ if (ret) {
+ dev_err(gc05a2->dev, "exposure ctrl range update failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gc05a2_update_pad_format(struct gc05a2 *gc08a3,
+ const struct gc05a2_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->code = GC05A2_MBUS_CODE;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int gc05a2_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ struct v4l2_rect *crop;
+ const struct gc05a2_mode *mode;
+
+ mode = v4l2_find_nearest_size(gc05a2_modes, ARRAY_SIZE(gc05a2_modes),
+ width, height, fmt->format.width,
+ fmt->format.height);
+
+ /* update crop info to subdev state */
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ crop->width = mode->width;
+ crop->height = mode->height;
+
+ /* update fmt info to subdev state */
+ gc05a2_update_pad_format(gc05a2, mode, &fmt->format);
+ mbus_fmt = v4l2_subdev_state_get_format(state, 0);
+ *mbus_fmt = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+ gc05a2->cur_mode = mode;
+ gc05a2_update_cur_mode_controls(gc05a2, mode);
+
+ return 0;
+}
+
+static int gc05a2_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, 0);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC05A2_NATIVE_WIDTH;
+ sel->r.height = GC05A2_NATIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gc05a2_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ .code = GC05A2_MBUS_CODE,
+ .width = gc05a2_modes[0].width,
+ .height = gc05a2_modes[0].height,
+ },
+ };
+
+ gc05a2_set_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static int gc05a2_set_ctrl_hflip(struct gc05a2 *gc05a2, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc05a2->dev, "read hflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG,
+ GC05A2_FLIP_H_MASK,
+ ctrl_val ? GC05A2_FLIP_H_MASK : 0, NULL);
+}
+
+static int gc05a2_set_ctrl_vflip(struct gc05a2 *gc05a2, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc05a2->dev, "read vflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG,
+ GC05A2_FLIP_V_MASK,
+ ctrl_val ? GC05A2_FLIP_V_MASK : 0, NULL);
+}
+
+static int gc05a2_test_pattern(struct gc05a2 *gc05a2, u32 pattern_menu)
+{
+ u32 pattern;
+ int ret;
+
+ if (pattern_menu) {
+ switch (pattern_menu) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ pattern = pattern_menu << 4;
+ break;
+
+ case 8:
+ pattern = 0;
+ break;
+
+ case 9:
+ pattern = 4;
+ break;
+
+ default:
+ pattern = 0x00;
+ break;
+ }
+
+ ret = cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_IDX,
+ pattern, NULL);
+ if (ret)
+ return ret;
+
+ return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN,
+ GC05A2_TEST_PATTERN_EN, NULL);
+ } else {
+ return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN,
+ 0x00, NULL);
+ }
+}
+
+static int gc05a2_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gc05a2 *gc05a2 =
+ container_of(ctrl->handler, struct gc05a2, ctrls);
+ int ret = 0;
+ s64 exposure_max;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
+
+ state = v4l2_subdev_get_locked_active_state(&gc05a2->sd);
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = format->height + ctrl->val - GC05A2_EXP_MARGIN;
+ __v4l2_ctrl_modify_range(gc05a2->exposure,
+ gc05a2->exposure->minimum,
+ exposure_max, gc05a2->exposure->step,
+ exposure_max);
+ }
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is on for streaming.
+ */
+ if (!pm_runtime_get_if_active(gc05a2->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(gc05a2->regmap, GC05A2_EXP_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(gc05a2->regmap, GC05A2_AGAIN_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_VBLANK:
+ ret = cci_write(gc05a2->regmap, GC05A2_FRAME_LENGTH_REG,
+ gc05a2->cur_mode->height + ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_HFLIP:
+ ret = gc05a2_set_ctrl_hflip(gc05a2, ctrl->val);
+ break;
+
+ case V4L2_CID_VFLIP:
+ ret = gc05a2_set_ctrl_vflip(gc05a2, ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = gc05a2_test_pattern(gc05a2, ctrl->val);
+ break;
+
+ default:
+ break;
+ }
+
+ pm_runtime_put(gc05a2->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops gc05a2_ctrl_ops = {
+ .s_ctrl = gc05a2_set_ctrl,
+};
+
+static int gc05a2_start_streaming(struct gc05a2 *gc05a2)
+{
+ const struct gc05a2_mode *mode;
+ const struct gc05a2_reg_list *reg_list;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(gc05a2->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = cci_multi_reg_write(gc05a2->regmap,
+ mode_table_common,
+ ARRAY_SIZE(mode_table_common), NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ mode = gc05a2->cur_mode;
+ reg_list = &mode->reg_list;
+
+ ret = cci_multi_reg_write(gc05a2->regmap,
+ reg_list->regs, reg_list->num_of_regs, NULL);
+ if (ret < 0)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&gc05a2->ctrls);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "could not sync v4l2 controls\n");
+ goto err_rpm_put;
+ }
+
+ ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 1, NULL);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "write STREAMING_REG failed: %d\n", ret);
+ goto err_rpm_put;
+ }
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(gc05a2->dev);
+ return ret;
+}
+
+static int gc05a2_stop_streaming(struct gc05a2 *gc05a2)
+{
+ int ret;
+
+ ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 0, NULL);
+ if (ret < 0)
+ dev_err(gc05a2->dev, "could not sent stop streaming %d\n", ret);
+
+ pm_runtime_put(gc05a2->dev);
+ return ret;
+}
+
+static int gc05a2_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct gc05a2 *gc05a2 = to_gc05a2(subdev);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (enable)
+ ret = gc05a2_start_streaming(gc05a2);
+ else
+ ret = gc05a2_stop_streaming(gc05a2);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops gc05a2_video_ops = {
+ .s_stream = gc05a2_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops gc05a2_subdev_pad_ops = {
+ .enum_mbus_code = gc05a2_enum_mbus_code,
+ .enum_frame_size = gc05a2_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = gc05a2_set_format,
+ .get_selection = gc05a2_get_selection,
+};
+
+static const struct v4l2_subdev_core_ops gc05a2_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops gc05a2_subdev_ops = {
+ .core = &gc05a2_core_ops,
+ .video = &gc05a2_video_ops,
+ .pad = &gc05a2_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops gc05a2_internal_ops = {
+ .init_state = gc05a2_init_state,
+};
+
+static int gc05a2_get_regulators(struct device *dev, struct gc05a2 *gc05a2)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(gc05a2_supply_name); i++)
+ gc05a2->supplies[i].supply = gc05a2_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+}
+
+static int gc05a2_parse_fwnode(struct gc05a2 *gc05a2)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ int ret;
+ struct device *dev = gc05a2->dev;
+
+ endpoint =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ if (ret) {
+ dev_err(dev, "parsing endpoint node failed\n");
+ goto done;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ gc05a2_link_freq_menu_items,
+ ARRAY_SIZE(gc05a2_link_freq_menu_items),
+ &gc05a2->link_freq_bitmap);
+ if (ret)
+ goto done;
+
+done:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ fwnode_handle_put(endpoint);
+ return ret;
+}
+
+static u64 gc05a2_to_pixel_rate(u32 f_index)
+{
+ u64 pixel_rate =
+ gc05a2_link_freq_menu_items[f_index] * 2 * GC05A2_DATA_LANES;
+
+ return div_u64(pixel_rate, GC05A2_RGB_DEPTH);
+}
+
+static int gc05a2_init_controls(struct gc05a2 *gc05a2)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc05a2->sd);
+ const struct gc05a2_mode *mode = &gc05a2_modes[0];
+ const struct v4l2_ctrl_ops *ops = &gc05a2_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ctrl_hdlr = &gc05a2->ctrls;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+ if (ret)
+ return ret;
+
+ gc05a2->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ gc05a2->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &gc05a2->hflip);
+
+ gc05a2->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &gc05a2_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(gc05a2_link_freq_menu_items) - 1,
+ 0,
+ gc05a2_link_freq_menu_items);
+ if (gc05a2->link_freq)
+ gc05a2->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ gc05a2->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc05a2_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ gc05a2_to_pixel_rate(0),
+ 1,
+ gc05a2_to_pixel_rate(0));
+
+ gc05a2->vblank =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc05a2_ctrl_ops, V4L2_CID_VBLANK,
+ mode->vts_min - mode->height,
+ GC05A2_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ h_blank = mode->hts - mode->width;
+ gc05a2->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_HBLANK, h_blank, h_blank, 1,
+ h_blank);
+ if (gc05a2->hblank)
+ gc05a2->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, GC05A2_AGAIN_MIN,
+ GC05A2_AGAIN_MAX, GC05A2_AGAIN_STEP,
+ GC05A2_AGAIN_MIN);
+
+ exposure_max = mode->vts_def - GC05A2_EXP_MARGIN;
+ gc05a2->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_EXPOSURE, GC05A2_EXP_MIN,
+ exposure_max, GC05A2_EXP_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(gc05a2_test_pattern_menu) - 1,
+ 0, 0, gc05a2_test_pattern_menu);
+
+ /* register properties to fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
+ if (ret)
+ goto error_ctrls;
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto error_ctrls;
+ }
+
+ gc05a2->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_ctrls:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int gc05a2_identify_module(struct gc05a2 *gc05a2)
+{
+ u64 val;
+ int ret;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_REG_CHIP_ID, &val, NULL);
+ if (ret) {
+ dev_err(gc05a2->dev, "failed to read chip id");
+ return ret;
+ }
+
+ if (val != GC05A2_CHIP_ID) {
+ dev_err(gc05a2->dev, "chip id mismatch: 0x%x!=0x%llx",
+ GC05A2_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int gc05a2_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gc05a2 *gc05a2;
+ int ret;
+
+ gc05a2 = devm_kzalloc(dev, sizeof(*gc05a2), GFP_KERNEL);
+ if (!gc05a2)
+ return -ENOMEM;
+
+ gc05a2->dev = dev;
+
+ ret = gc05a2_parse_fwnode(gc05a2);
+ if (ret)
+ return ret;
+
+ gc05a2->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(gc05a2->regmap))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->regmap),
+ "failed to init CCI\n");
+
+ gc05a2->xclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(gc05a2->xclk))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->xclk),
+ "failed to get xclk\n");
+
+ ret = clk_set_rate(gc05a2->xclk, GC05A2_DEFAULT_CLK_FREQ);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set xclk frequency\n");
+
+ ret = gc05a2_get_regulators(dev, gc05a2);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get regulators\n");
+
+ gc05a2->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gc05a2->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->reset_gpio),
+ "failed to get gpio\n");
+
+ v4l2_i2c_subdev_init(&gc05a2->sd, client, &gc05a2_subdev_ops);
+ gc05a2->sd.internal_ops = &gc05a2_internal_ops;
+ gc05a2->cur_mode = &gc05a2_modes[0];
+ ret = gc05a2_power_on(gc05a2->dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to sensor power on\n");
+
+ ret = gc05a2_identify_module(gc05a2);
+ if (ret) {
+ dev_err(&client->dev, "failed to find sensor: %d\n", ret);
+ goto err_power_off;
+ }
+
+ ret = gc05a2_init_controls(gc05a2);
+ if (ret) {
+ dev_err(&client->dev, "failed to init controls: %d", ret);
+ goto err_power_off;
+ }
+
+ gc05a2->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ gc05a2->pad.flags = MEDIA_PAD_FL_SOURCE;
+ gc05a2->sd.dev = &client->dev;
+ gc05a2->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&gc05a2->sd.entity, 1, &gc05a2->pad);
+ if (ret < 0) {
+ dev_err(dev, "could not register media entity\n");
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ gc05a2->sd.state_lock = gc05a2->ctrls.lock;
+ ret = v4l2_subdev_init_finalize(&gc05a2->sd);
+ if (ret < 0) {
+ dev_err(dev, "v4l2 subdev init error: %d\n", ret);
+ goto err_media_entity_cleanup;
+ }
+
+ pm_runtime_set_active(gc05a2->dev);
+ pm_runtime_enable(gc05a2->dev);
+ pm_runtime_set_autosuspend_delay(gc05a2->dev, 1000);
+ pm_runtime_use_autosuspend(gc05a2->dev);
+ pm_runtime_idle(gc05a2->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&gc05a2->sd);
+ if (ret < 0) {
+ dev_err(dev, "could not register v4l2 device\n");
+ goto err_rpm;
+ }
+
+ return 0;
+
+err_rpm:
+ pm_runtime_disable(gc05a2->dev);
+ v4l2_subdev_cleanup(&gc05a2->sd);
+
+err_media_entity_cleanup:
+ media_entity_cleanup(&gc05a2->sd.entity);
+
+err_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&gc05a2->ctrls);
+
+err_power_off:
+ gc05a2_power_off(gc05a2->dev);
+
+ return ret;
+}
+
+static void gc05a2_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+
+ v4l2_async_unregister_subdev(&gc05a2->sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&gc05a2->sd.entity);
+ v4l2_ctrl_handler_free(&gc05a2->ctrls);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ gc05a2_power_off(gc05a2->dev);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct of_device_id gc05a2_of_match[] = {
+ { .compatible = "galaxycore,gc05a2" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gc05a2_of_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(gc05a2_pm_ops,
+ gc05a2_power_off,
+ gc05a2_power_on,
+ NULL);
+
+static struct i2c_driver gc05a2_i2c_driver = {
+ .driver = {
+ .of_match_table = gc05a2_of_match,
+ .pm = pm_ptr(&gc05a2_pm_ops),
+ .name = "gc05a2",
+ },
+ .probe = gc05a2_probe,
+ .remove = gc05a2_remove,
+};
+module_i2c_driver(gc05a2_i2c_driver);
+
+MODULE_DESCRIPTION("GalaxyCore gc05a2 Camera driver");
+MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>");
+MODULE_LICENSE("GPL");
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 1/2] media: dt-bindings: i2c: add GalaxyCore GC05A2 image sensor
From: Zhi Mao @ 2024-03-23 1:47 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek,
Krzysztof Kozlowski
In-Reply-To: <20240323014751.4989-1-zhi.mao@mediatek.com>
Add YAML device tree binding for GC05A2 CMOS image sensor,
and the relevant MAINTAINERS entries.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
.../bindings/media/i2c/galaxycore,gc05a2.yaml | 112 ++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
new file mode 100644
index 000000000000..0e7a7b5ac89f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (c) 2023 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc05a2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GalaxyCore gc05a2 1/5" 5M Pixel MIPI CSI-2 sensor
+
+maintainers:
+ - Zhi Mao <zhi.mao@mediatek.com>
+
+description:
+ The gc05a2 is a raw image sensor with an MIPI CSI-2 image data
+ interface and CCI (I2C compatible) control bus. The output format
+ is raw Bayer.
+
+properties:
+ compatible:
+ const: galaxycore,gc05a2
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dovdd-supply: true
+
+ avdd-supply: true
+
+ dvdd-supply: true
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the RESETB pin.
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+ description:
+ Output port node, single endpoint describing the CSI-2 transmitter.
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ oneOf:
+ - items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+ - items:
+ - const: 1
+ - const: 2
+
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - dovdd-supply
+ - avdd-supply
+ - dvdd-supply
+ - reset-gpios
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@37 {
+ compatible = "galaxycore,gc05a2";
+ reg = <0x37>;
+
+ clocks = <&gc05a2_clk>;
+
+ reset-gpios = <&pio 21 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&gc05a2_avdd>;
+ dovdd-supply = <&gc05a2_dovdd>;
+ dvdd-supply = <&gc05a2_dvdd>;
+
+ port {
+ sensor_out: endpoint {
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <448000000 224000000>;
+ remote-endpoint = <&seninf_csi_port_1_in>;
+ };
+ };
+ };
+ };
+
+...
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 0/2] media: i2c: Add support for GC05A2 sensor
From: Zhi Mao @ 2024-03-23 1:47 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
This series adds YAML DT binding and V4L2 sub-device driver for Galaxycore's
GC05A2 5-megapixel 10-bit RAW CMOS 1/5" sensor, with an MIPI CSI-2 image data
interface and the I2C control bus.
The driver is implemented with V4L2 framework.
- Async registered as a V4L2 sub-device.
- As the first component of camera system including Seninf, ISP pipeline.
- A media entity that provides one source pad in common.
- Used in camera features on ChromeOS application.
Also this driver supports following features:
- manual exposure and analog gain control support
- vertical blanking control support
- test pattern support
- media controller support
- runtime PM support
- support resolution: 2592x1944@30fps, 1280x720@60fps
Previous versions of this patch-set can be found here:
v1:https://lore.kernel.org/linux-media/20240316025253.2300-1-zhi.mao@mediatek.com/
v0:https://lore.kernel.org/linux-media/20240313054409.8073-1-zhi.mao@mediatek.com/
This series is based on linux-next, tag: next-20240323
Changes in v2:
- gc05a2 sensor driver:
-- use function: pm_runtime_get_if_active()
Thanks
Zhi Mao (2):
media: dt-bindings: i2c: add GalaxyCore GC05A2 image sensor
media: i2c: Add GC05A2 image sensor driver
.../bindings/media/i2c/galaxycore,gc05a2.yaml | 112 ++
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc05a2.c | 1387 +++++++++++++++++
4 files changed, 1510 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
create mode 100644 drivers/media/i2c/gc05a2.c
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] media: mediatek: vcodec: Handle invalid decoder vsi
From: Irui Wang (王瑞) @ 2024-03-23 1:45 UTC (permalink / raw)
To: matthias.bgg@gmail.com, mchehab@kernel.org,
sebastian.fricke@collabora.com,
angelogioacchino.delregno@collabora.com,
nicolas.dufresne@collabora.com, hverkuil-cisco@xs4all.nl,
Yunfei Dong (董云飞)
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-media@vger.kernel.org,
Maoguang Meng (孟毛广),
linux-mediatek@lists.infradead.org,
Longfei Wang (王龙飞),
Project_Global_Chrome_Upstream_Group
In-Reply-To: <6420a5c39e0a6f85d4fd5a85643d601bd1e2fb05.camel@collabora.com>
Dear Nicolas,
Thanks for your reviewing.
On Fri, 2024-03-22 at 10:44 -0400, Nicolas Dufresne wrote:
> Hi,
>
> Le mercredi 20 mars 2024 à 14:13 +0800, Irui Wang a écrit :
> > Handle invalid decoder vsi in vpu_dec_init to ensure the decoder
> > vsi is
> > valid for future use.
> >
> > Signed-off-by: Irui Wang <irui.wang@mediatek.com>
> > ---
> > .../media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c | 6
> > ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git
> > a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> > b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> > index 82e57ae983d5..17770993fe5a 100644
> > --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> > +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
> > @@ -231,6 +231,12 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
> > mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu);
> >
> > err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
> > +
> > + if (IS_ERR_OR_NULL(vpu->vsi)) {
> > + mtk_vdec_err(vpu->ctx, "invalid vdec vsi, status=%d",
> > err);
> > + return -EINVAL;
> > + }
> > +
>
> Make sense, though on the cosmetic side, were is the err value from
> if the vsi
> pointer is NULL ?
Do you mean the returned 'err'?, it's the status value returned by
'send message'. Even if the 'vsi' is NULL, the returned status value
'err' may still be 'NO_ERROR', there is no direct relationship between
them.
If I misunderstood your question, please correct me. Thank you very
much.
>
> Nicolas
>
> > mtk_vdec_debug(vpu->ctx, "- ret=%d", err);
> > return err;
> > }
>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox