Re: [PATCH 0/3] USB Mux support for Chipidea
On 2017-07-14 23:27, Stephen Boyd wrote: > Quoting Stephen Boyd (2017-07-13 15:35:02) >> Quoting Peter Rosin (2017-07-11 22:04:46) >>> >>> Maybe no need for a compatible update either, if it works to do something >>> like this in the DT? >>> >>> usb_switch: usb-switch { >>> compatible = "gpio-mux"; >>> mux-gpios = <_gpios 4 GPIO_ACTIVE_HIGH>, >>> <_gpios XXX GPIO_ACTIVE_XXX>; >>> idle-state = <2>; >>> #mux-control-cells = <0>; >>> pinctrl-names = "default"; >>> pinctrl-0 = <_sw_sel_pm>; >>> }; >>> >>> But I obviously know little about how things are wired and really works, >>> so that might be totally off... >>> >>> Otherwise, maybe a generic mux-pinctrl driver would do the trick? >>> (compare with drivers/i2c/muxes/i2c-mux-pinctrl.c) >>> >> >> Agreed. > > Testing looks good when I use the gpio-mux binding. The only thing I Glad to hear it, I didn't really want a new driver so similar to the mux-gpio driver... > noticed is that gpio-mux driver is requesting the gpio with > GPIOD_OUT_LOW. Is that intentional? Not really intentional, it was just easy. > I worry that may randomly mux the > D+/D- lines during probe if the gpio is asserted at probe time. It isn't > a problem for me right now, because the mux is power on defaulted to > have the gpio deasserted, but it may be a problem if the default > changes. It will not change, the only change I will accept is if the code in mux-gpio can be arranged to request the gpios with the idle-state from the start. But even then, the default idle-state (0) will not change. So, you should be safe. I will look at the new patches later. Cheers, Peter
Re: [PATCH] drivers: i2c: muxes: Kconfig
On 2017-07-12 19:31, Chris Gorman wrote: > Kconfig says the resulting module is pinctrl-i2cmux and the module when > built is i2c-mux-pinctrl. > > Signed-off-by: Chris GormanYes, thanks! I took the liberty of changing the Subject to "i2c: mux: pinctrl: mention the correct module name in Kconfig help text" and applied it to my for-current branch. Cheers, Peter
[PATCH v2] mux: mux-core: unregister mux_class in mux_exit()
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppusw...@linux.intel.com> Fixes an obvious and nasty typo. Fixes: a3b02a9c6591 ("mux: minimal mux subsystem") Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppusw...@linux.intel.com> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/mux-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Hi Greg! Please pick this up and pass on along with the other patch that removed the unloved Kconfig question [1]. Thanks! Cheers, peda [1] https://lkml.org/lkml/2017/7/4/118 diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c index 90b8995f07cb..2fe96c470112 100644 --- a/drivers/mux/mux-core.c +++ b/drivers/mux/mux-core.c @@ -46,7 +46,7 @@ static int __init mux_init(void) static void __exit mux_exit(void) { - class_register(_class); + class_unregister(_class); ida_destroy(_ida); } -- 2.1.4
Re: [PATCH v1 1/1] mux: consumer: Add dummy functions for !CONFIG_MULTIPLEXER case
On 2017-07-09 01:04, Kuppuswamy, Sathyanarayanan wrote: > Hi Peter, > > On 7/8/2017 1:55 PM, Peter Rosin wrote: >> On 2017-07-07 23:41, sathyanarayanan.kuppusw...@linux.intel.com wrote: >>> From: Kuppuswamy Sathyanarayanan >>> <sathyanarayanan.kuppusw...@linux.intel.com> >>> >>> Add dummy functions to avoid compile time issues when CONFIG_MULTIPLEXER >>> is not enabled. >> Hi! >> >> Consumers should "select MULTIPLEXER", > If their driver can't work without mux_* calls then you can make it > compulsory. But its not always true. >> so this does not make sense. >> Or do you have a driver that has an optional mux consumer? > I came across this case when I was working on Intel USB MUX driver. I > think you know the history behind it. Although I am not planning to > merge that driver now, but I think the use case is still valid. Yeah, it's a valid use case. But why add a facility that noone uses? Sure, if there's an actual consumer that needs it. But there isn't... See, I have spent considerable time taking stuff like this out in order to get the thing merged at all. I even think I wrote dummy inlines like this at some point (but I'm not sure if I actually wrote them and I don't think I submitted them. But I did think about it, that's for sure). Anyway, I'm not very happy about ballooning the core with support for non-essentials just yet. Maybe my mind-set will change over time? (And no, I don't *know* the history behind the "Intel USB MUX driver", I e.g. never saw the consumer code. And I have the feeling that stuff were discussed in other threads that I was not part of and some (most?) questions I asked about it was left unanswered.) Cheers, peda
Re: [PATCH v2 1/1] mux: consumer: Add dummy functions for !CONFIG_MULTIPLEXER case
On 2017-07-08 23:22, Andy Shevchenko wrote: > On Sat, Jul 8, 2017 at 9:12 PM, >wrote: >> From: Kuppuswamy Sathyanarayanan >> >> Add dummy functions to avoid compile time issues when CONFIG_MULTIPLEXER >> is not enabled. >> > > I don't think the error return code is okay to all of them. The return > value should be choosen carefully (for some functions it's okay IMO to > return 0). BTW, is ENODEV correct for this situation? I have this nagging feeling that ENODEV is over-used? And again, all these stubs should all be inlines, or things will break it this file is included more than once. >> Signed-off-by: Kuppuswamy Sathyanarayanan >> >> --- >> include/linux/mux/consumer.h | 38 ++ >> 1 file changed, 38 insertions(+) >> >> Changes since v1: >> * Changed #ifdef to #if IS_ENABLED. >> >> diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h >> index 5577e1b..df78988 100644 >> --- a/include/linux/mux/consumer.h >> +++ b/include/linux/mux/consumer.h >> @@ -16,6 +16,7 @@ >> struct device; >> struct mux_control; >> >> +#if IS_ENABLED(CONFIG_MULTIPLEXER) >> unsigned int mux_control_states(struct mux_control *mux); >> int __must_check mux_control_select(struct mux_control *mux, >> unsigned int state); >> @@ -29,4 +30,41 @@ void mux_control_put(struct mux_control *mux); >> struct mux_control *devm_mux_control_get(struct device *dev, >> const char *mux_name); >> >> +#else >> +unsigned int mux_control_states(struct mux_control *mux) >> +{ >> + return -ENODEV; > > Peter, is here we are obliged to return error code in such case? Since it will presumably be difficult to obtain a mux_control w/o the mux-core being present, it doesn't matter much what most of these stubs return. For this stub, 0 is perhaps best, since the kernel-doc for mux_control_states mentions nothing about any error possibility. >> +} >> + >> +int __must_check mux_control_select(struct mux_control *mux, >> + unsigned int state) >> +{ >> + return -ENODEV; > > return 0; ? Maybe. But it doesn't matter much, but in this case the consumer must handle errors. See above. >> +} >> + >> +int __must_check mux_control_try_select(struct mux_control *mux, >> + unsigned int state) >> +{ >> + return -ENODEV; >> +} > > return 0; ? Maybe. But it doesn't matter much, but in this case the consumer must handle errors. See above. >> + >> +int mux_control_deselect(struct mux_control *mux) >> +{ >> + return -ENODEV; >> +} > > return 0; ? Probably. See above. Cheers, peda >> + >> +struct mux_control *mux_control_get(struct device *dev, const char >> *mux_name) >> +{ >> + return ERR_PTR(-ENODEV); >> +} >> + >> +void mux_control_put(struct mux_control *mux) {} >> + >> +struct mux_control *devm_mux_control_get(struct device *dev, >> +const char *mux_name) >> +{ >> + return ERR_PTR(-ENODEV); >> +} >> +#endif >> + >> #endif /* _LINUX_MUX_CONSUMER_H */ >> -- >> 2.7.4 >> > > >
Re: [PATCH v2 1/1] mux: mux-core: Add NULL check for dev->of_node
On 2017-07-09 01:12, Kuppuswamy, Sathyanarayanan wrote: > Hi Peter, > > > On 7/8/2017 2:00 PM, Peter Rosin wrote: >> On 2017-07-07 23:46, sathyanarayanan.kuppusw...@linux.intel.com wrote: >>> From: Kuppuswamy Sathyanarayanan >>> <sathyanarayanan.kuppusw...@linux.intel.com> >>> >>> If dev->of_node is NULL, then calling mux_control_get() >>> function can lead to NULL pointer exception. So adding >>> a NULL check for dev->of_node. >>> >>> Signed-off-by: Kuppuswamy Sathyanarayanan >>> <sathyanarayanan.kuppusw...@linux.intel.com> >> Do you have a driver that might call mux_control_get and not have any >> of_node? > For non-device tree drivers, this case is valid. I hit this issue when I > was working on Intel USB MUX driver. >> If not, I don't see the point of this check. > Since this is an API for other consumers, I think its better to have > some sanity checks. > > If a non device tree driver call this API , I think its better to fail > with some error no instead of creating null pointer exception. Is it? When authoring a new driver, and you make some error like this, why is a "nice" error better than a big fat fail? If you get a null deref, you will presumably also get a call stack etc, which will help you find where you made the error, w/o adding a bunch of traces to find out exactly what you did wrong. So, I'm skeptic... Cheers, peda >> >> Cheers, >> peda >> >>> --- >>> drivers/mux/mux-core.c | 3 +++ >>> 1 file changed, 3 insertions(+) >>> >>> Changes since v1: >>> * Removed dummy new line. >>> >>> diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c >>> index 90b8995..924c983 100644 >>> --- a/drivers/mux/mux-core.c >>> +++ b/drivers/mux/mux-core.c >>> @@ -438,6 +438,9 @@ struct mux_control *mux_control_get(struct device *dev, >>> const char *mux_name) >>> int index = 0; >>> int ret; >>> >>> + if (!np) >>> + return ERR_PTR(-ENODEV); >>> + >>> if (mux_name) { >>> index = of_property_match_string(np, "mux-control-names", >>> mux_name); >>> >
Re: [PATCH v2 1/3] mux: Add mux_control_get_optional() API
On 2017-07-19 04:08, Stephen Boyd wrote: > Quoting Peter Rosin (2017-07-17 01:20:14) >> On 2017-07-14 23:40, Stephen Boyd wrote: >>> diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c >>> index 90b8995f07cb..a0e5bf16f02f 100644 >>> --- a/drivers/mux/mux-core.c >>> +++ b/drivers/mux/mux-core.c >>> @@ -289,6 +289,9 @@ EXPORT_SYMBOL_GPL(devm_mux_chip_register); >>> */ >>> unsigned int mux_control_states(struct mux_control *mux) >>> { >>> + if (!mux) >>> + return 0; >>> + >> >> I don't think this is appropriate. For this function, it might be ok, >> but... >> >>> return mux->states; >>> } >>> EXPORT_SYMBOL_GPL(mux_control_states); >>> @@ -338,6 +341,9 @@ int mux_control_select(struct mux_control *mux, >>> unsigned int state) >>> { >>> int ret; >>> >>> + if (!mux) >>> + return 0; >>> + >> >> ...here and for other cases below it's very odd to return "ok", when >> -EINVAL or something seems much more appropriate. And if -EINVAL is >> returned here, the benefit of returning fake values for anything >> pretty much falls apart. >> >> I simply don't like it, and prefer if the consumer code is arranged >> to not call the mux functions when the optional get() does not find >> the mux. > > Do you want the callers of the mux APIs to know that an optional mux > isn't there, and then have checks at all callsites on optional muxes to > make sure consumers don't call the mux functions? Won't that duplicate > lots of checks in drivers for something the core could treat as a don't > care case? Sorry, I don't understand why the consumer cares that it was > there or not when it is optional. Ok, I had a look around to figure out how others handle this, and e.g. gpio has (ugly) macros (VALIDATE_DESC) to handle this. I guess you are right and I'm wrong. So, please keep all the if (!mux) checks. Thanks for insisting! >> >>> ret = down_killable(>lock); >>> if (ret < 0) >>> return ret; >>> @@ -370,6 +376,9 @@ int mux_control_try_select(struct mux_control *mux, >>> unsigned int state) >>> { >>> int ret; >>> >>> + if (!mux) >>> + return 0; >>> + >>> if (down_trylock(>lock)) >>> return -EBUSY; >>> >>> @@ -398,6 +407,9 @@ int mux_control_deselect(struct mux_control *mux) >>> { >>> int ret = 0; >>> >>> + if (!mux) >>> + return 0; >>> + >>> if (mux->idle_state != MUX_IDLE_AS_IS && >>> mux->idle_state != mux->cached_state) >>> ret = mux_control_set(mux, mux->idle_state); >>> @@ -422,14 +434,8 @@ static struct mux_chip >>> *of_find_mux_chip_by_node(struct device_node *np) >>> return dev ? to_mux_chip(dev) : NULL; >>> } >>> >>> -/** >>> - * mux_control_get() - Get the mux-control for a device. >>> - * @dev: The device that needs a mux-control. >>> - * @mux_name: The name identifying the mux-control. >>> - * >>> - * Return: A pointer to the mux-control, or an ERR_PTR with a negative >>> errno. >>> - */ >>> -struct mux_control *mux_control_get(struct device *dev, const char >>> *mux_name) >>> +struct mux_control * >>> +__mux_control_get(struct device *dev, const char *mux_name, bool optional) >>> { >>> struct device_node *np = dev->of_node; >>> struct of_phandle_args args; >>> @@ -441,6 +447,8 @@ struct mux_control *mux_control_get(struct device *dev, >>> const char *mux_name) >>> if (mux_name) { >>> index = of_property_match_string(np, "mux-control-names", >>>mux_name); >>> + if (index == -EINVAL && optional) >>> + return NULL; >> >> What about -ENODATA? > > At this point in the code we're finding the index of a mux-control-names > property so I was trying to handle the case where there isn't a > mux-control-names property at all Yes, you indeed need to check for -EINVAL to catch that. No argument about that. > but we're looking for something > optional anyway. If there is a property, then we would see some other > error if so
Re: [PATCH] drm: atmel-hlcdc: use a default gamma ramp if none is specified
On 2017-07-03 14:02, Boris Brezillon wrote: > On Mon, 3 Jul 2017 13:53:28 +0200 > Peter Rosin <p...@axentia.se> wrote: > >> On 2017-07-03 13:31, Boris Brezillon wrote: >>> On Mon, 3 Jul 2017 11:42:10 +0200 >>> Peter Rosin <p...@axentia.se> wrote: >>> >>>> At init and if the gamma_lut property is ever removed, the clut >>>> registers must be programmed with a default gamma ramp instead of >>>> being left in some unknown state. >>>> >>>> Fixes: 364a7bf574eb ("drm: atmel-hlcdc: add support for 8-bit color lookup >>>> table mode") >>>> Signed-off-by: Peter Rosin <p...@axentia.se> >>>> --- >>>> drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 17 - >>>> 1 file changed, 16 insertions(+), 1 deletion(-) >>>> >>>> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >>>> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >>>> index b5bd9b0..0ccd93c 100644 >>>> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >>>> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >>>> @@ -429,6 +429,14 @@ static void atmel_hlcdc_plane_update_format(struct >>>> atmel_hlcdc_plane *plane, >>>>ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); >>>> } >>>> >>>> +static void atmel_hlcdc_default_gamma_ramp(struct atmel_hlcdc_layer >>>> *layer) >>>> +{ >>>> + int idx; >>>> + >>>> + for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++) >>>> + atmel_hlcdc_layer_write_clut(layer, idx, idx * 0x10101); >>>> +} >>>> + >>>> static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane) >>>> { >>>>struct drm_crtc *crtc = plane->base.crtc; >>>> @@ -438,9 +446,14 @@ static void atmel_hlcdc_plane_update_clut(struct >>>> atmel_hlcdc_plane *plane) >>>>if (!crtc || !crtc->state) >>>>return; >>>> >>>> - if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) >>>> + if (!crtc->state->color_mgmt_changed) >>>>return; >>>> >>>> + if (!crtc->state->gamma_lut) { >>>> + atmel_hlcdc_default_gamma_ramp(>layer); >>> >>> Hm, I'd prefer to have state->gamma_lut properly initialized in >>> atmel_hlcdc_crtc_reset(), this way you don't have to do that in the >>> update path. >> >> The gamma_lut property can be removed, so you have to handle it here anyway. >> No? > > Hm, what do you mean by removed? AFAICT, a property, once attached to > a DRM object, exists until the DRM object is destroyed. The data > attached to this property (here, the gamma_lut array) can be NULL, but > once you have allocated the data container, it will be duplicated (and > possibly updated) every time an atomic operation is triggered. By remove I meant someone somehow triggering a call like this: drm_atomic_crtc_set_property(crtc, ...->gamma_lut_property, 0); By the looks of it, that is not happening? But I'm not sure of that, and even if it's not happening today, that may change... > By initializing this field in crtc->reset(), you enforce the > default/reset state, which IIUC, is what you want here. Agreed, if it is not possible to remove/clear out the gamma_lut property, then the only thing needed is to initialize the hw clut registers to the linear ramp somewhere early. But is it indeed a fact that there is no way to clear out the gamma_lut prop? Cheers, peda
[PATCH v2] mux: remove the Kconfig question for the subsystem
The MULTIPLEXER question in the Kconfig might be confusing and is of dubious value. Remove it. This makes consumers responsible for selecting MULTIPLEXER, which they already do. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig | 19 +-- 1 file changed, 5 insertions(+), 14 deletions(-) On 2017-07-04 08:00, Linus Torvalds wrote: > On Jul 3, 2017 22:53, "Peter Rosin" <p...@axentia.se > <mailto:p...@axentia.se>> wrote: >> But ok, is something like this what you wanted? > > I *think* this week just result in an empty menu if there are > no drivers selecting it. > > Shouldn't the 'if' be outside the menu? But I didn't test > anything, since I'm not in front of my computer any more.. Right, the previous patch also had the problem that it removed the MULTIPLEXER option completely and was therefore total crap. I have tested this patch more thoroughly and it should be a definite improvement. Sorry for the noise... Cheers, peda diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 7c754a0..19e4e90 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -2,20 +2,11 @@ # Multiplexer devices # -menuconfig MULTIPLEXER - tristate "Multiplexer subsystem" - help - Multiplexer controller subsystem. Multiplexers are used in a - variety of settings, and this subsystem abstracts their use - so that the rest of the kernel sees a common interface. When - multiple parallel multiplexers are controlled by one single - multiplexer controller, this subsystem also coordinates the - multiplexer accesses. - - To compile the subsystem as a module, choose M here: the module will - be called mux-core. +config MULTIPLEXER + tristate -if MULTIPLEXER +menu "Multiplexer drivers" + depends on MULTIPLEXER config MUX_ADG792A tristate "Analog Devices ADG792A/ADG792G Multiplexers" @@ -56,4 +47,4 @@ config MUX_MMIO To compile the driver as a module, choose M here: the module will be called mux-mmio. -endif +endmenu -- 2.1.4
[PATCH] mux: remove the Kconfig question for the subsystem
The MULTIPLEXER question in the Kconfig might be confusing and is of dubious value. Remove it. This makes consumers responsible for selecting MULTIPLEXER, which they already do. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig | 15 +++ 1 file changed, 3 insertions(+), 12 deletions(-) Hi Linus! My thinking was that I wanted it to be possible to select mux drivers before any mux consumer was selected. I also wanted to avoid one question for each of the mux drivers when the whole thing is not needed most of the time... But ok, is something like this what you wanted? Cheers, peda diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 7c754a0..96d364e 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -2,18 +2,7 @@ # Multiplexer devices # -menuconfig MULTIPLEXER - tristate "Multiplexer subsystem" - help - Multiplexer controller subsystem. Multiplexers are used in a - variety of settings, and this subsystem abstracts their use - so that the rest of the kernel sees a common interface. When - multiple parallel multiplexers are controlled by one single - multiplexer controller, this subsystem also coordinates the - multiplexer accesses. - - To compile the subsystem as a module, choose M here: the module will - be called mux-core. +menu "Multiplexer support" if MULTIPLEXER @@ -57,3 +46,5 @@ config MUX_MMIO be called mux-mmio. endif + +endmenu -- 2.1.4
[PATCH v3 05/16] drm/fb-helper: do a generic fb_setcmap helper in terms of crtc .gamma_set
This makes the redundant fb helpers .load_lut, .gamma_set and .gamma_get completely obsolete. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_fb_helper.c | 165 +++- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b75b1f2..7f8199a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1257,27 +1257,6 @@ void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); -static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, u16 regno, struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_framebuffer *fb = fb_helper->fb; - - /* -* The driver really shouldn't advertise pseudo/directcolor -* visuals if it can't deal with the palette. -*/ - if (WARN_ON(!fb_helper->funcs->gamma_set || - !fb_helper->funcs->gamma_get)) - return -EINVAL; - - WARN_ON(fb->format->cpp[0] != 1); - - fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); - - return 0; -} - static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) { u32 *palette = (u32 *)info->pseudo_palette; @@ -1310,54 +1289,68 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) return 0; } -/** - * drm_fb_helper_setcmap - implementation for _ops.fb_setcmap - * @cmap: cmap to set - * @info: fbdev registered by the helper - */ -int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) +static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - const struct drm_crtc_helper_funcs *crtc_funcs; - u16 *red, *green, *blue, *transp; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, j, rc = 0; - int start; + int i, ret = 0; - if (oops_in_progress) - return -EBUSY; + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + if (!crtc->funcs->gamma_set || !crtc->gamma_size) + return -EINVAL; - mutex_lock(_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { - mutex_unlock(_helper->lock); - return -EBUSY; - } + if (cmap->start + cmap->len > crtc->gamma_size) + return -EINVAL; - drm_modeset_lock_all(dev); - if (info->fix.visual == FB_VISUAL_TRUECOLOR) { - rc = setcmap_pseudo_palette(cmap, info); - goto out; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); + memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); + memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); + + ret = crtc->funcs->gamma_set(crtc, r, g, b, +crtc->gamma_size, NULL); + if (ret) + return ret; } - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; - crtc_funcs = crtc->helper_private; + return ret; +} - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; +static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct drm_device *dev = fb_helper->dev; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_crtc *crtc; + u16 *r, *g, *b; + int i, ret = 0; - if (!crtc->gamma_size) { - rc = -EINVAL; + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + drm_modeset_acquire_init(, 0); +retry: + ret = drm_modeset_lock_all_ctx(dev, ); + if (ret) + goto fini; + state->acquire_ctx = + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + if (!crtc->funcs->gamma_set) { + ret = -EINVAL; goto out; } -
[PATCH v3 00/16] improve the fb_setcmap helper
Hi! While trying to get CLUT support for the atmel_hlcdc driver, and specifically for the emulated fbdev interface, I received some push-back that my feeble in-driver attempts should be solved by the core. This is my attempt to do it right. I have obviously not tested all of this with more than a compile, but patches 1 through 5 are enough to make the atmel-hlcdc driver do what I need. The rest is just lots of removals and cleanup made possible by the improved core. Please test, I would not be surprised if I have fouled up some bit-manipulation somewhere, or if I have misunderstood something about atomics... Changes since v2: - Added patch 1/16 which factors out pseudo-palette handling. - Removed the if (cmap->start + cmap->len < cmap->start) sanity check on the assumption that the fbdev core handles it. - Added patch 4/16 which factors out atomic state and commit handling from drm_atomic_helper_legacy_gamma_set to drm_mode_gamma_set_ioctl. - Do one atomic commit for all affected crtc. - Removed a now obsolete note in include/drm/drm_crtc.h (ammended the last patch). - Cc list is getting long, so I have redused the list for the individual patches. If you would like to get the full series (or nothing at all) for the next round (if that is needed) just say so. Changes since v1: - Rebased to next-20170621 - Split 1/11 into a preparatory patch, a cleanup patch and then the meat in 3/14. - Handle pseudo-palette for FB_VISUAL_TRUECOLOR. - Removed the empty .gamma_get/.gamma_set fb helpers from the armada driver that I had somehow managed to ignore but which 0day found real quick. - Be less judgemental on drivers only providing .gamma_get and .gamma_set, but no .load_lut. That's actually a valid thing to do if you only need pseudo-palette for FB_VISUAL_TRUECOLOR. - Add a comment about colliding bitfields in the nouveau driver. - Remove gamma_set/gamma_get declarations from the radeon driver (the definitions were removed in v1). Cheers, peda Peter Rosin (16): drm/fb-helper: factor out pseudo-palette drm/fb-helper: keep the .gamma_store updated in drm_fb_helper_setcmap drm/fb-helper: remove drm_fb_helper_save_lut_atomic drm/color-mgmt: move atomic state/commit out from .gamma_set drm/fb-helper: do a generic fb_setcmap helper in terms of crtc .gamma_set drm: amd: remove dead code and pointless local lut storage drm: armada: remove dead empty functions drm: ast: remove dead code and pointless local lut storage drm: cirrus: remove dead code and pointless local lut storage drm: gma500: remove dead code and pointless local lut storage drm: i915: remove dead code and pointless local lut storage drm: mgag200: remove dead code and pointless local lut storage drm: nouveau: remove dead code and pointless local lut storage drm: radeon: remove dead code and pointless local lut storage drm: stm: remove dead code and pointless local lut storage drm: remove unused and redundant callbacks drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 24 drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h| 1 - drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 29 ++--- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 29 ++--- drivers/gpu/drm/amd/amdgpu/dce_v6_0.c | 29 ++--- drivers/gpu/drm/amd/amdgpu/dce_v8_0.c | 29 ++--- drivers/gpu/drm/amd/amdgpu/dce_virtual.c| 25 +--- drivers/gpu/drm/armada/armada_crtc.c| 10 -- drivers/gpu/drm/armada/armada_crtc.h| 2 - drivers/gpu/drm/armada/armada_fbdev.c | 2 - drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_fb.c| 20 --- drivers/gpu/drm/ast/ast_mode.c | 28 +--- drivers/gpu/drm/cirrus/cirrus_drv.h | 8 -- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 - drivers/gpu/drm/cirrus/cirrus_mode.c| 73 +++ drivers/gpu/drm/drm_atomic_helper.c | 37 ++ drivers/gpu/drm/drm_color_mgmt.c| 27 +++- drivers/gpu/drm/drm_fb_helper.c | 195 +--- drivers/gpu/drm/gma500/framebuffer.c| 22 drivers/gpu/drm/gma500/gma_display.c| 34 ++--- drivers/gpu/drm/gma500/gma_display.h| 2 +- drivers/gpu/drm/gma500/psb_intel_display.c | 7 +- drivers/gpu/drm/gma500/psb_intel_drv.h | 1 - drivers/gpu/drm/i915/intel_drv.h| 1 - drivers/gpu/drm/i915/intel_fbdev.c | 31 - drivers/gpu/drm/mgag200/mgag200_drv.h | 5 - drivers/gpu/drm/mgag200/mgag200_fb.c| 2 - drivers/gpu/drm/mgag200/mgag200_mode.c | 64 +++-- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 28 ++-- drivers/gpu/drm/nouveau/nouveau_crtc.h | 3 - drivers/gpu/drm/nouveau/nouveau_fbcon.c | 22 drivers/gpu/drm/nouveau/nv50_display.c | 42 ++ drivers/gpu/drm/radeon/atombios_crtc.c | 1 - drivers/gpu/drm/radeon/radeon_connectors.c | 7 +- drivers/gpu/drm/radeon/rade
[PATCH v3 03/16] drm/fb-helper: remove drm_fb_helper_save_lut_atomic
drm_fb_helper_save_lut_atomic is redundant since the .gamma_store is now always kept up to date by drm_fb_helper_setcmap. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_fb_helper.c | 17 - 1 file changed, 17 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 41fd9e0..b75b1f2 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -253,22 +253,6 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); -static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) -{ - uint16_t *r_base, *g_base, *b_base; - int i; - - if (helper->funcs->gamma_get == NULL) - return; - - r_base = crtc->gamma_store; - g_base = r_base + crtc->gamma_size; - b_base = g_base + crtc->gamma_size; - - for (i = 0; i < crtc->gamma_size; i++) - helper->funcs->gamma_get(crtc, _base[i], _base[i], _base[i], i); -} - static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; @@ -309,7 +293,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info) if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) continue; - drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); funcs->mode_set_base_atomic(mode_set->crtc, mode_set->fb, mode_set->x, -- 2.1.4
Re: [PATCH v3 01/16] drm/fb-helper: factor out pseudo-palette
On 2017-07-04 12:36, Peter Rosin wrote: > The pseudo-palette has nothing to do with the crtc, so move it > out of the crtc loop and update the palette once, then break out > early. > > Signed-off-by: Peter Rosin <p...@axenita.se> Should of course be p...@axentia.se I wonder when I managed to Ctrl-T that one? Cheers, peda
[PATCH v3 09/16] drm: cirrus: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/cirrus/cirrus_drv.h | 8 drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 - drivers/gpu/drm/cirrus/cirrus_mode.c | 71 --- 3 files changed, 16 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 8690352..be2d7e48 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -96,7 +96,6 @@ struct cirrus_crtc { struct drm_crtc base; - u8 lut_r[256], lut_g[256], lut_b[256]; int last_dpms; boolenabled; }; @@ -180,13 +179,6 @@ cirrus_bo(struct ttm_buffer_object *bo) #define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base) #define DRM_FILE_PAGE_OFFSET (0x1ULL >> PAGE_SHIFT) - /* cirrus_mode.c */ -void cirrus_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, int regno); -void cirrus_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, -u16 *blue, int regno); - - /* cirrus_main.c */ int cirrus_device_init(struct cirrus_device *cdev, struct drm_device *ddev, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 7fa58ee..1fedab0 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -265,8 +265,6 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, } static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { - .gamma_set = cirrus_crtc_fb_gamma_set, - .gamma_get = cirrus_crtc_fb_gamma_get, .fb_probe = cirrusfb_create, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 2b4c2c3..92ff7de 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -31,25 +31,6 @@ * This file contains setup code for the CRTC. */ -static void cirrus_crtc_load_lut(struct drm_crtc *crtc) -{ - struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct cirrus_device *cdev = dev->dev_private; - int i; - - if (!crtc->enabled) - return; - - for (i = 0; i < CIRRUS_LUT_SIZE; i++) { - /* VGA registers */ - WREG8(PALETTE_INDEX, i); - WREG8(PALETTE_DATA, cirrus_crtc->lut_r[i]); - WREG8(PALETTE_DATA, cirrus_crtc->lut_g[i]); - WREG8(PALETTE_DATA, cirrus_crtc->lut_b[i]); - } -} - /* * The DRM core requires DPMS functions, but they make little sense in our * case and so are just stubs @@ -330,15 +311,25 @@ static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_crtc_state *state) { - struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + u16 *r, *g, *b; int i; - for (i = 0; i < size; i++) { - cirrus_crtc->lut_r[i] = red[i]; - cirrus_crtc->lut_g[i] = green[i]; - cirrus_crtc->lut_b[i] = blue[i]; + if (!crtc->enabled) + return 0; + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + for (i = 0; i < CIRRUS_LUT_SIZE; i++) { + /* VGA registers */ + WREG8(PALETTE_INDEX, i); + WREG8(PALETTE_DATA, *r++ >> 8); + WREG8(PALETTE_DATA, *g++ >> 8); + WREG8(PALETTE_DATA, *b++ >> 8); } - cirrus_crtc_load_lut(crtc); return 0; } @@ -365,7 +356,6 @@ static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .mode_set_base = cirrus_crtc_mode_set_base, .prepare = cirrus_crtc_prepare, .commit = cirrus_crtc_commit, - .load_lut = cirrus_crtc_load_lut, }; /* CRTC setup */ @@ -373,7 +363,6 @@ static void cirrus_crtc_init(struct drm_device *dev) { struct cirrus_device *cdev = dev->dev_private; struct cirrus_crtc *cirrus_crtc; - int i; cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)), @@ -387,37 +376,9 @@ static void cirrus_crtc_init(st
[PATCH v3 13/16] drm: nouveau: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 26 - drivers/gpu/drm/nouveau/nouveau_crtc.h | 3 --- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 22 -- drivers/gpu/drm/nouveau/nv50_display.c | 40 +++-- 4 files changed, 22 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index f562824..b233412 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -764,13 +764,18 @@ nv_crtc_gamma_load(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct drm_device *dev = nv_crtc->base.dev; struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; + u16 *r, *g, *b; int i; rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + for (i = 0; i < 256; i++) { - rgbs[i].r = nv_crtc->lut.r[i] >> 8; - rgbs[i].g = nv_crtc->lut.g[i] >> 8; - rgbs[i].b = nv_crtc->lut.b[i] >> 8; + rgbs[i].r = *r++ >> 8; + rgbs[i].g = *g++ >> 8; + rgbs[i].b = *b++ >> 8; } nouveau_hw_load_state_palette(dev, nv_crtc->index, _display(dev)->mode_reg); @@ -792,13 +797,6 @@ nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, struct drm_crtc_state *state) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - nv_crtc->lut.r[i] = r[i]; - nv_crtc->lut.g[i] = g[i]; - nv_crtc->lut.b[i] = b[i]; - } /* We need to know the depth before we upload, but it's possible to * get called before a framebuffer is bound. If this is the case, @@ -1095,7 +1093,6 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { .mode_set = nv_crtc_mode_set, .mode_set_base = nv04_crtc_mode_set_base, .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, - .load_lut = nv_crtc_gamma_load, .disable = nv_crtc_disable, }; @@ -1103,17 +1100,12 @@ int nv04_crtc_create(struct drm_device *dev, int crtc_num) { struct nouveau_crtc *nv_crtc; - int ret, i; + int ret; nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); if (!nv_crtc) return -ENOMEM; - for (i = 0; i < 256; i++) { - nv_crtc->lut.r[i] = i << 8; - nv_crtc->lut.g[i] = i << 8; - nv_crtc->lut.b[i] = i << 8; - } nv_crtc->lut.depth = 0; nv_crtc->index = crtc_num; diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index 050fcf3..b7a18fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -61,9 +61,6 @@ struct nouveau_crtc { struct { struct nouveau_bo *nvbo; - uint16_t r[256]; - uint16_t g[256]; - uint16_t b[256]; int depth; } lut; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 2665a07..f770784 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -278,26 +278,6 @@ nouveau_fbcon_accel_init(struct drm_device *dev) info->fbops = _fbcon_ops; } -static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - nv_crtc->lut.r[regno] = red; - nv_crtc->lut.g[regno] = green; - nv_crtc->lut.b[regno] = blue; -} - -static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - *red = nv_crtc->lut.r[regno]; - *green = nv_crtc->lut.g[regno]; - *blue = nv_crtc->lut.b[regno]; -} - static void nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) { @@ -467,8 +447,6 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) } static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { - .gamma_set = nouveau_fbcon_gamma_set, - .gamma_get = nouveau_fbcon_gamma_get,
[PATCH v3 11/16] drm: i915: remove dead code and pointless local lut storage
The driver stores lut values from the fbdev interface, and is able to give them back, but does not appear to do anything with these lut values. The generic fb helpers have replaced this function, and may even have made the driver work for the C8 mode from the fbdev interface. But that is untested. Since the fb helpers .gamma_set and .gamma_get are obsolete, remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/i915/intel_drv.h | 1 - drivers/gpu/drm/i915/intel_fbdev.c | 31 --- 2 files changed, 32 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d93efb4..bc7bfa0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -786,7 +786,6 @@ struct intel_crtc { struct drm_crtc base; enum pipe pipe; enum plane plane; - u8 lut_r[256], lut_g[256], lut_b[256]; /* * Whether the crtc and the connected output pipeline is active. Implies * that crtc->enabled is set, i.e. the current mode configuration has diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 03347c6..5bac953 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -281,27 +281,6 @@ static int intelfb_create(struct drm_fb_helper *helper, return ret; } -/** Sets the color ramps on behalf of RandR */ -static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - intel_crtc->lut_r[regno] = red >> 8; - intel_crtc->lut_g[regno] = green >> 8; - intel_crtc->lut_b[regno] = blue >> 8; -} - -static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - *red = intel_crtc->lut_r[regno] << 8; - *green = intel_crtc->lut_g[regno] << 8; - *blue = intel_crtc->lut_b[regno] << 8; -} - static struct drm_fb_helper_crtc * intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) { @@ -370,7 +349,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, struct drm_connector *connector; struct drm_encoder *encoder; struct drm_fb_helper_crtc *new_crtc; - struct intel_crtc *intel_crtc; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; @@ -412,13 +390,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, num_connectors_enabled++; - intel_crtc = to_intel_crtc(connector->state->crtc); - for (j = 0; j < 256; j++) { - intel_crtc->lut_r[j] = j; - intel_crtc->lut_g[j] = j; - intel_crtc->lut_b[j] = j; - } - new_crtc = intel_fb_helper_crtc(fb_helper, connector->state->crtc); @@ -519,8 +490,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { .initial_config = intel_fb_initial_config, - .gamma_set = intel_crtc_fb_gamma_set, - .gamma_get = intel_crtc_fb_gamma_get, .fb_probe = intelfb_create, }; -- 2.1.4
[PATCH v3 14/16] drm: radeon: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/radeon/atombios_crtc.c | 1 - drivers/gpu/drm/radeon/radeon_connectors.c | 7 ++- drivers/gpu/drm/radeon/radeon_display.c | 71 - drivers/gpu/drm/radeon/radeon_fb.c | 2 - drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 1 - drivers/gpu/drm/radeon/radeon_mode.h| 4 -- 6 files changed, 33 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 3c492a0..02baaaf 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -2217,7 +2217,6 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = { .mode_set_base_atomic = atombios_crtc_set_base_atomic, .prepare = atombios_crtc_prepare, .commit = atombios_crtc_commit, - .load_lut = radeon_crtc_load_lut, .disable = atombios_crtc_disable, }; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 27affbd..2f642cb 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -773,12 +773,15 @@ static int radeon_connector_set_property(struct drm_connector *connector, struct if (connector->encoder->crtc) { struct drm_crtc *crtc = connector->encoder->crtc; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); radeon_crtc->output_csc = radeon_encoder->output_csc; - (*crtc_funcs->load_lut)(crtc); + /* +* Our .gamma_set assumes the .gamma_store has been +* prefilled and don't care about its arguments. +*/ + crtc->funcs->gamma_set(crtc, NULL, NULL, NULL, 0, NULL); } } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0ea575d..3d7394d4 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -42,6 +42,7 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; + u16 *r, *g, *b; int i; DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); @@ -60,11 +61,14 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc) WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x003f); WREG8(AVIVO_DC_LUT_RW_INDEX, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(AVIVO_DC_LUT_30_COLOR, -(radeon_crtc->lut_r[i] << 20) | -(radeon_crtc->lut_g[i] << 10) | -(radeon_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } /* Only change bit 0 of LUT_SEL, other bits are set elsewhere */ @@ -76,6 +80,7 @@ static void dce4_crtc_load_lut(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; + u16 *r, *g, *b; int i; DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); @@ -93,11 +98,14 @@ static void dce4_crtc_load_lut(struct drm_crtc *crtc) WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x0007); WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, - (radeon_crtc->lut_r[i] << 20) | - (radeon_crtc->lut_g[i] << 10) | - (radeon_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } } @@ -106,6 +114,7 @@ static void dce5_crtc_load_lut(struct drm_crtc *crtc) struct rad
[PATCH v3 16/16] drm: remove unused and redundant callbacks
Drivers no longer have any need for these callbacks, and there are no users. Zap. Zap-zap-zzzap-p-pp-p. Signed-off-by: Peter Rosin <p...@axentia.se> --- include/drm/drm_crtc.h | 8 include/drm/drm_fb_helper.h | 32 include/drm/drm_modeset_helper_vtables.h | 16 3 files changed, 56 deletions(-) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d442d30..7bb0a6e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -358,14 +358,6 @@ struct drm_crtc_funcs { * drm_crtc_enable_color_mgmt(), which then supports the legacy gamma * interface through the drm_atomic_helper_legacy_gamma_set() * compatibility implementation. -* -* NOTE: -* -* Drivers that support gamma tables and also fbdev emulation through -* the provided helper library need to take care to fill out the gamma -* hooks for both. Currently there's a bit an unfortunate duplication -* going on, which should eventually be unified to just one set of -* hooks. */ int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size, struct drm_crtc_state *state); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index a5ea6ff..33fe959 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -85,38 +85,6 @@ struct drm_fb_helper_surface_size { */ struct drm_fb_helper_funcs { /** -* @gamma_set: -* -* Set the given gamma LUT register on the given CRTC. -* -* This callback is optional. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno); - /** -* @gamma_get: -* -* Read the given gamma LUT register on the given CRTC, used to save the -* current LUT when force-restoring the fbdev for e.g. kdbg. -* -* This callback is optional. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno); - - /** * @fb_probe: * * Driver callback to allocate and initialize the fbdev info structure. diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 85984b2..0773db9 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -267,22 +267,6 @@ struct drm_crtc_helper_funcs { enum mode_set_atomic); /** -* @load_lut: -* -* Load a LUT prepared with the _fb_helper_funcs.gamma_set vfunc. -* -* This callback is optional and is only used by the fbdev emulation -* helpers. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*load_lut)(struct drm_crtc *crtc); - - /** * @disable: * * This callback should be used to disable the CRTC. With the atomic -- 2.1.4
[PATCH v3 10/16] drm: gma500: remove dead code and pointless local lut storage
The redundant fb helpers .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/gma500/framebuffer.c | 22 drivers/gpu/drm/gma500/gma_display.c | 32 ++ drivers/gpu/drm/gma500/psb_intel_display.c | 7 +-- drivers/gpu/drm/gma500/psb_intel_drv.h | 1 - 4 files changed, 12 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 7da70b6..2570c7f 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -479,26 +479,6 @@ static struct drm_framebuffer *psb_user_framebuffer_create return psb_framebuffer_create(dev, cmd, r); } -static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - - gma_crtc->lut_r[regno] = red >> 8; - gma_crtc->lut_g[regno] = green >> 8; - gma_crtc->lut_b[regno] = blue >> 8; -} - -static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, int regno) -{ - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - - *red = gma_crtc->lut_r[regno] << 8; - *green = gma_crtc->lut_g[regno] << 8; - *blue = gma_crtc->lut_b[regno] << 8; -} - static int psbfb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { @@ -525,8 +505,6 @@ static int psbfb_probe(struct drm_fb_helper *helper, } static const struct drm_fb_helper_funcs psb_fb_helper_funcs = { - .gamma_set = psbfb_gamma_set, - .gamma_get = psbfb_gamma_get, .fb_probe = psbfb_probe, }; diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index ccf8c33..3bf65d4 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -144,33 +144,32 @@ void gma_crtc_load_lut(struct drm_crtc *crtc) struct gma_crtc *gma_crtc = to_gma_crtc(crtc); const struct psb_offset *map = _priv->regmap[gma_crtc->pipe]; int palreg = map->palette; + u16 *r, *g, *b; int i; /* The clocks have to be on to load the palette. */ if (!crtc->enabled) return; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + if (gma_power_begin(dev, false)) { for (i = 0; i < 256; i++) { REG_WRITE(palreg + 4 * i, - ((gma_crtc->lut_r[i] + - gma_crtc->lut_adj[i]) << 16) | - ((gma_crtc->lut_g[i] + - gma_crtc->lut_adj[i]) << 8) | - (gma_crtc->lut_b[i] + - gma_crtc->lut_adj[i])); + (((*r++ >> 8) + gma_crtc->lut_adj[i]) << 16) | + (((*g++ >> 8) + gma_crtc->lut_adj[i]) << 8) | + ((*b++ >> 8) + gma_crtc->lut_adj[i])); } gma_power_end(dev); } else { for (i = 0; i < 256; i++) { /* FIXME: Why pipe[0] and not pipe[..._crtc->pipe]? */ dev_priv->regs.pipe[0].palette[i] = - ((gma_crtc->lut_r[i] + - gma_crtc->lut_adj[i]) << 16) | - ((gma_crtc->lut_g[i] + - gma_crtc->lut_adj[i]) << 8) | - (gma_crtc->lut_b[i] + - gma_crtc->lut_adj[i]); + (((*r++ >> 8) + gma_crtc->lut_adj[i]) << 16) | + (((*g++ >> 8) + gma_crtc->lut_adj[i]) << 8) | + ((*b++ >> 8) + gma_crtc->lut_adj[i]); } } @@ -180,15 +179,6 @@ int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, u32 size, struct drm_crtc_state *state) { - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - gma_crtc->lut_r[i] = red[i] >> 8; - gma_crtc->lut_g[i] = green[i] >> 8; - gma_crtc->lut_b[i] = blue[i] >> 8; - } -
[PATCH v3 08/16] drm: ast: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_fb.c | 20 drivers/gpu/drm/ast/ast_mode.c | 26 ++ 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 8880f0b..569a148 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -245,7 +245,6 @@ struct ast_connector { struct ast_crtc { struct drm_crtc base; - u8 lut_r[256], lut_g[256], lut_b[256]; struct drm_gem_object *cursor_bo; uint64_t cursor_addr; int cursor_width, cursor_height; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 4ad4acd..dbabcac 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -255,27 +255,7 @@ static int astfb_create(struct drm_fb_helper *helper, return ret; } -static void ast_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - ast_crtc->lut_r[regno] = red >> 8; - ast_crtc->lut_g[regno] = green >> 8; - ast_crtc->lut_b[regno] = blue >> 8; -} - -static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - *red = ast_crtc->lut_r[regno] << 8; - *green = ast_crtc->lut_g[regno] << 8; - *blue = ast_crtc->lut_b[regno] << 8; -} - static const struct drm_fb_helper_funcs ast_fb_helper_funcs = { - .gamma_set = ast_fb_gamma_set, - .gamma_get = ast_fb_gamma_get, .fb_probe = astfb_create, }; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 6f0335b..e686072 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -63,15 +63,18 @@ static inline void ast_load_palette_index(struct ast_private *ast, static void ast_crtc_load_lut(struct drm_crtc *crtc) { struct ast_private *ast = crtc->dev->dev_private; - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + u16 *r, *g, *b; int i; if (!crtc->enabled) return; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + for (i = 0; i < 256; i++) - ast_load_palette_index(ast, i, ast_crtc->lut_r[i], - ast_crtc->lut_g[i], ast_crtc->lut_b[i]); + ast_load_palette_index(ast, i, *r++ >> 8, *g++ >> 8, *b++ >> 8); } static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mode *mode, @@ -633,7 +636,6 @@ static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { .mode_set = ast_crtc_mode_set, .mode_set_base = ast_crtc_mode_set_base, .disable = ast_crtc_disable, - .load_lut = ast_crtc_load_lut, .prepare = ast_crtc_prepare, .commit = ast_crtc_commit, @@ -648,15 +650,6 @@ static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_crtc_state *state) { - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - int i; - - /* userspace palettes are always correct as is */ - for (i = 0; i < size; i++) { - ast_crtc->lut_r[i] = red[i] >> 8; - ast_crtc->lut_g[i] = green[i] >> 8; - ast_crtc->lut_b[i] = blue[i] >> 8; - } ast_crtc_load_lut(crtc); return 0; @@ -681,7 +674,6 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { static int ast_crtc_init(struct drm_device *dev) { struct ast_crtc *crtc; - int i; crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL); if (!crtc) @@ -690,12 +682,6 @@ static int ast_crtc_init(struct drm_device *dev) drm_crtc_init(dev, >base, _crtc_funcs); drm_mode_crtc_set_gamma_size(>base, 256); drm_crtc_helper_add(>base, _crtc_helper_funcs); - - for (i = 0; i < 256; i++) { - crtc->lut_r[i] = i; - crtc->lut_g[i] = i; - crtc->lut_b[i] = i; - } return 0; } -- 2.1.4
[PATCH v3 12/16] drm: mgag200: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/mgag200/mgag200_drv.h | 5 --- drivers/gpu/drm/mgag200/mgag200_fb.c | 2 -- drivers/gpu/drm/mgag200/mgag200_mode.c | 62 -- 3 files changed, 15 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index c88b6ec..04f1dfb 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -237,11 +237,6 @@ mgag200_bo(struct ttm_buffer_object *bo) { return container_of(bo, struct mgag200_bo, bo); } - /* mgag200_crtc.c */ -void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, int regno); -void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, -u16 *blue, int regno); /* mgag200_mode.c */ int mgag200_modeset_init(struct mga_device *mdev); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5d3b1fa..5cf980a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -258,8 +258,6 @@ static int mga_fbdev_destroy(struct drm_device *dev, } static const struct drm_fb_helper_funcs mga_fb_helper_funcs = { - .gamma_set = mga_crtc_fb_gamma_set, - .gamma_get = mga_crtc_fb_gamma_get, .fb_probe = mgag200fb_create, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 2141796..dc500ef 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -27,15 +27,19 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc) { - struct mga_crtc *mga_crtc = to_mga_crtc(crtc); struct drm_device *dev = crtc->dev; struct mga_device *mdev = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; + u16 *r_ptr, *g_ptr, *b_ptr; int i; if (!crtc->enabled) return; + r_ptr = crtc->gamma_store; + g_ptr = r_ptr + crtc->gamma_size; + b_ptr = g_ptr + crtc->gamma_size; + WREG8(DAC_INDEX + MGA1064_INDEX, 0); if (fb && fb->format->cpp[0] * 8 == 16) { @@ -46,25 +50,27 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc) if (i > (MGAG200_LUT_SIZE >> 1)) { r = b = 0; } else { - r = mga_crtc->lut_r[i << 1]; - b = mga_crtc->lut_b[i << 1]; + r = *r_ptr++ >> 8; + b = *b_ptr++ >> 8; + r_ptr++; + b_ptr++; } } else { - r = mga_crtc->lut_r[i]; - b = mga_crtc->lut_b[i]; + r = *r_ptr++ >> 8; + b = *b_ptr++ >> 8; } /* VGA registers */ WREG8(DAC_INDEX + MGA1064_COL_PAL, r); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); WREG8(DAC_INDEX + MGA1064_COL_PAL, b); } return; } for (i = 0; i < MGAG200_LUT_SIZE; i++) { /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_r[i]); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_b[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8); } } @@ -1399,14 +1405,6 @@ static int mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_crtc_state *state) { - struct mga_crtc *mga_crtc = to_mga_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - mga_crtc->lut_r[i] = red[i] >> 8; - mga_crtc->lut_g[i] = green[i] >> 8; - mga_crtc->lut_b[i] = blue[i] >> 8; -
[PATCH v3 15/16] drm: stm: remove dead code and pointless local lut storage
The redundant fb helper .load_lut is no longer used, and can not work right without also providing the fb helpers .gamma_set and .gamma_get thus rendering the code in this driver suspect. Just remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/stm/ltdc.c | 12 drivers/gpu/drm/stm/ltdc.h | 1 - 2 files changed, 13 deletions(-) diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 1b9483d..87829b9 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -375,17 +375,6 @@ static irqreturn_t ltdc_irq(int irq, void *arg) * DRM_CRTC */ -static void ltdc_crtc_load_lut(struct drm_crtc *crtc) -{ - struct ltdc_device *ldev = crtc_to_ltdc(crtc); - unsigned int i, lay; - - for (lay = 0; lay < ldev->caps.nb_layers; lay++) - for (i = 0; i < 256; i++) - reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS, - ldev->clut[i]); -} - static void ltdc_crtc_enable(struct drm_crtc *crtc) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); @@ -523,7 +512,6 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, } static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { - .load_lut = ltdc_crtc_load_lut, .enable = ltdc_crtc_enable, .disable = ltdc_crtc_disable, .mode_set_nofb = ltdc_crtc_mode_set_nofb, diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index d7a9c73..620ca55 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -27,7 +27,6 @@ struct ltdc_device { struct drm_panel *panel; struct mutex err_lock; /* protecting error_status */ struct ltdc_caps caps; - u32 clut[256]; /* color look up table */ u32 error_status; u32 irq_status; }; -- 2.1.4
[PATCH v3 04/16] drm/color-mgmt: move atomic state/commit out from .gamma_set
Handle the atomics directly in the ioctl instead, in preparation for the fb_setcmap helper needing to commit the gamma map for several crtc in one commit. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/dce_v6_0.c| 2 +- drivers/gpu/drm/amd/amdgpu/dce_v8_0.c| 2 +- drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 2 +- drivers/gpu/drm/ast/ast_mode.c | 2 +- drivers/gpu/drm/cirrus/cirrus_mode.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 37 +++- drivers/gpu/drm/drm_color_mgmt.c | 27 ++- drivers/gpu/drm/gma500/gma_display.c | 2 +- drivers/gpu/drm/gma500/gma_display.h | 2 +- drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 2 +- drivers/gpu/drm/nouveau/nv50_display.c | 2 +- drivers/gpu/drm/radeon/radeon_display.c | 2 +- drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 2 +- include/drm/drm_atomic_helper.h | 2 +- include/drm/drm_crtc.h | 3 +-- 20 files changed, 52 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 9f78c03..31c977b 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2622,7 +2622,7 @@ static void dce_v10_0_cursor_reset(struct drm_crtc *crtc) static int dce_v10_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) + struct drm_crtc_state *state) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 4bcf01d..cf42640 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2642,7 +2642,7 @@ static void dce_v11_0_cursor_reset(struct drm_crtc *crtc) static int dce_v11_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) + struct drm_crtc_state *state) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index fd134a4..045014d 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -2494,7 +2494,7 @@ static void dce_v6_0_cursor_reset(struct drm_crtc *crtc) static int dce_v6_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) + struct drm_crtc_state *state) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index a9e8695..ee9389f 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2473,7 +2473,7 @@ static void dce_v8_0_cursor_reset(struct drm_crtc *crtc) static int dce_v8_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) + struct drm_crtc_state *state) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index 90bb083..f194dfc 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -166,7 +166,7 @@ static void dce_virtual_bandwidth_update(struct amdgpu_device *adev) static int dce_virtual_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) + struct drm_crtc_state *state) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index aaef0a6..6f0335b 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -646,7 +646,7 @@ static void ast_crtc_reset(struct drm_crtc *crtc) static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16
[PATCH v3 06/16] drm: amd: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 24 drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 1 - drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v6_0.c| 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v8_0.c| 27 +++ drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 23 --- 7 files changed, 28 insertions(+), 128 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index c0d8c6f..7dc3780 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -312,31 +312,7 @@ static int amdgpu_fbdev_destroy(struct drm_device *dev, struct amdgpu_fbdev *rfb return 0; } -/** Sets the color ramps on behalf of fbcon */ -static void amdgpu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - - amdgpu_crtc->lut_r[regno] = red >> 6; - amdgpu_crtc->lut_g[regno] = green >> 6; - amdgpu_crtc->lut_b[regno] = blue >> 6; -} - -/** Gets the color ramps on behalf of fbcon */ -static void amdgpu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - - *red = amdgpu_crtc->lut_r[regno] << 6; - *green = amdgpu_crtc->lut_g[regno] << 6; - *blue = amdgpu_crtc->lut_b[regno] << 6; -} - static const struct drm_fb_helper_funcs amdgpu_fb_helper_funcs = { - .gamma_set = amdgpu_crtc_fb_gamma_set, - .gamma_get = amdgpu_crtc_fb_gamma_get, .fb_probe = amdgpufb_create, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 43a9d3a..39f7eda 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -369,7 +369,6 @@ struct amdgpu_atom_ss { struct amdgpu_crtc { struct drm_crtc base; int crtc_id; - u16 lut_r[256], lut_g[256], lut_b[256]; bool enabled; bool can_tile; uint32_t crtc_offset; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 31c977b..717be5f 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2267,6 +2267,7 @@ static void dce_v10_0_crtc_load_lut(struct drm_crtc *crtc) struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); struct drm_device *dev = crtc->dev; struct amdgpu_device *adev = dev->dev_private; + u16 *r, *g, *b; int i; u32 tmp; @@ -2304,11 +2305,14 @@ static void dce_v10_0_crtc_load_lut(struct drm_crtc *crtc) WREG32(mmDC_LUT_WRITE_EN_MASK + amdgpu_crtc->crtc_offset, 0x0007); WREG32(mmDC_LUT_RW_INDEX + amdgpu_crtc->crtc_offset, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(mmDC_LUT_30_COLOR + amdgpu_crtc->crtc_offset, - (amdgpu_crtc->lut_r[i] << 20) | - (amdgpu_crtc->lut_g[i] << 10) | - (amdgpu_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } tmp = RREG32(mmDEGAMMA_CONTROL + amdgpu_crtc->crtc_offset); @@ -2624,15 +2628,6 @@ static int dce_v10_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_crtc_state *state) { - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - int i; - - /* userspace palettes are always correct as is */ - for (i = 0; i < size; i++) { - amdgpu_crtc->lut_r[i] = red[i] >> 6; - amdgpu_crtc->lut_g[i] = green[i] >> 6; - amdgpu_crtc->lut_b[i] = blue[i] >> 6; - } dce_v10_0_crtc_load_lut(crtc); return 0; @@ -2844,14 +2839,12 @@ static const struct drm_crtc_helper_funcs dce_v10_0_crtc_helper_funcs = { .mode_set_base_atomic = dce_v10_0_crtc_set_base_atomic, .prepare = dce_v10_0_crtc_prepare, .commi
[PATCH v3 02/16] drm/fb-helper: keep the .gamma_store updated in drm_fb_helper_setcmap
I think the gamma_store can end up invalid on error. But the way I read it, that can happen in drm_mode_gamma_set_ioctl as well, so why should this pesky legacy fbdev stuff be any better? Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_fb_helper.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 9c76b8c..41fd9e0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1339,6 +1339,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) const struct drm_crtc_helper_funcs *crtc_funcs; u16 *red, *green, *blue, *transp; struct drm_crtc *crtc; + u16 *r, *g, *b; int i, j, rc = 0; int start; @@ -1367,6 +1368,24 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) transp = cmap->transp; start = cmap->start; + if (!crtc->gamma_size) { + rc = -EINVAL; + goto out; + } + + if (cmap->start + cmap->len > crtc->gamma_size) { + rc = -EINVAL; + goto out; + } + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); + memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); + memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); + for (j = 0; j < cmap->len; j++) { u16 hred, hgreen, hblue, htransp = 0x; -- 2.1.4
[PATCH v3 07/16] drm: armada: remove dead empty functions
The redundant fb helpers .gamma_set and .gamma_get are no longer used. Remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/armada/armada_crtc.c | 10 -- drivers/gpu/drm/armada/armada_crtc.h | 2 -- drivers/gpu/drm/armada/armada_fbdev.c | 2 -- 3 files changed, 14 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 4fe19fd..96bccf8 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -334,16 +334,6 @@ static void armada_drm_vblank_off(struct armada_crtc *dcrtc) armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); } -void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, - int idx) -{ -} - -void armada_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - int idx) -{ -} - /* The mode_config.mutex will be held for this call */ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) { diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 7e8906d..bab11f4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -102,8 +102,6 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); -void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 602dfea..5fa076d 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -118,8 +118,6 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, } static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { - .gamma_set = armada_drm_crtc_gamma_set, - .gamma_get = armada_drm_crtc_gamma_get, .fb_probe = armada_fb_probe, }; -- 2.1.4
[PATCH v3 01/16] drm/fb-helper: factor out pseudo-palette
The pseudo-palette has nothing to do with the crtc, so move it out of the crtc loop and update the palette once, then break out early. Signed-off-by: Peter Rosin <p...@axenita.se> --- drivers/gpu/drm/drm_fb_helper.c | 60 + 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a4cfef9..9c76b8c 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1280,29 +1280,6 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, struct drm_fb_helper *fb_helper = info->par; struct drm_framebuffer *fb = fb_helper->fb; - if (info->fix.visual == FB_VISUAL_TRUECOLOR) { - u32 *palette; - u32 value; - /* place color in psuedopalette */ - if (regno > 16) - return -EINVAL; - palette = (u32 *)info->pseudo_palette; - red >>= (16 - info->var.red.length); - green >>= (16 - info->var.green.length); - blue >>= (16 - info->var.blue.length); - value = (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset); - if (info->var.transp.length > 0) { - u32 mask = (1 << info->var.transp.length) - 1; - - mask <<= info->var.transp.offset; - value |= mask; - } - palette[regno] = value; - return 0; - } - /* * The driver really shouldn't advertise pseudo/directcolor * visuals if it can't deal with the palette. @@ -1318,6 +1295,38 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, return 0; } +static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) +{ + u32 *palette = (u32 *)info->pseudo_palette; + int i; + + if (cmap->start + cmap->len > 16) + return -EINVAL; + + for (i = 0; i < cmap->len; ++i) { + u16 red = cmap->red[i]; + u16 green = cmap->green[i]; + u16 blue = cmap->blue[i]; + u32 value; + + red >>= 16 - info->var.red.length; + green >>= 16 - info->var.green.length; + blue >>= 16 - info->var.blue.length; + value = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + + mask <<= info->var.transp.offset; + value |= mask; + } + palette[cmap->start + i] = value; + } + + return 0; +} + /** * drm_fb_helper_setcmap - implementation for _ops.fb_setcmap * @cmap: cmap to set @@ -1343,6 +1352,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } drm_modeset_lock_all(dev); + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + rc = setcmap_pseudo_palette(cmap, info); + goto out; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; crtc_funcs = crtc->helper_private; -- 2.1.4
[PATCH] drm: atmel-hlcdc: use a default gamma ramp if none is specified
At init and if the gamma_lut property is ever removed, the clut registers must be programmed with a default gamma ramp instead of being left in some unknown state. Fixes: 364a7bf574eb ("drm: atmel-hlcdc: add support for 8-bit color lookup table mode") Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 17 - 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index b5bd9b0..0ccd93c 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -429,6 +429,14 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); } +static void atmel_hlcdc_default_gamma_ramp(struct atmel_hlcdc_layer *layer) +{ + int idx; + + for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++) + atmel_hlcdc_layer_write_clut(layer, idx, idx * 0x10101); +} + static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane) { struct drm_crtc *crtc = plane->base.crtc; @@ -438,9 +446,14 @@ static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane) if (!crtc || !crtc->state) return; - if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) + if (!crtc->state->color_mgmt_changed) return; + if (!crtc->state->gamma_lut) { + atmel_hlcdc_default_gamma_ramp(>layer); + return; + } + lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) { @@ -918,6 +931,8 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, 0x40040890); } + atmel_hlcdc_default_gamma_ramp(>layer); + return 0; } -- 2.1.4
Re: [PATCH] drm: atmel-hlcdc: use a default gamma ramp if none is specified
On 2017-07-03 13:31, Boris Brezillon wrote: > On Mon, 3 Jul 2017 11:42:10 +0200 > Peter Rosin <p...@axentia.se> wrote: > >> At init and if the gamma_lut property is ever removed, the clut >> registers must be programmed with a default gamma ramp instead of >> being left in some unknown state. >> >> Fixes: 364a7bf574eb ("drm: atmel-hlcdc: add support for 8-bit color lookup >> table mode") >> Signed-off-by: Peter Rosin <p...@axentia.se> >> --- >> drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 17 - >> 1 file changed, 16 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >> b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >> index b5bd9b0..0ccd93c 100644 >> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c >> @@ -429,6 +429,14 @@ static void atmel_hlcdc_plane_update_format(struct >> atmel_hlcdc_plane *plane, >> ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); >> } >> >> +static void atmel_hlcdc_default_gamma_ramp(struct atmel_hlcdc_layer *layer) >> +{ >> +int idx; >> + >> +for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++) >> +atmel_hlcdc_layer_write_clut(layer, idx, idx * 0x10101); >> +} >> + >> static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane) >> { >> struct drm_crtc *crtc = plane->base.crtc; >> @@ -438,9 +446,14 @@ static void atmel_hlcdc_plane_update_clut(struct >> atmel_hlcdc_plane *plane) >> if (!crtc || !crtc->state) >> return; >> >> -if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) >> +if (!crtc->state->color_mgmt_changed) >> return; >> >> +if (!crtc->state->gamma_lut) { >> +atmel_hlcdc_default_gamma_ramp(>layer); > > Hm, I'd prefer to have state->gamma_lut properly initialized in > atmel_hlcdc_crtc_reset(), this way you don't have to do that in the > update path. The gamma_lut property can be removed, so you have to handle it here anyway. No? Cheers, peda >> +return; >> +} >> + >> lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; >> >> for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) { >> @@ -918,6 +931,8 @@ static int atmel_hlcdc_plane_init_properties(struct >> atmel_hlcdc_plane *plane, >> 0x40040890); >> } >> >> +atmel_hlcdc_default_gamma_ramp(>layer); >> + >> return 0; >> } >> >
Re: [Intel-gfx] [PATCH v3 00/16] improve the fb_setcmap helper
On 2017-07-05 08:08, Daniel Vetter wrote: > On Tue, Jul 04, 2017 at 12:36:56PM +0200, Peter Rosin wrote: >> Hi! >> >> While trying to get CLUT support for the atmel_hlcdc driver, and >> specifically for the emulated fbdev interface, I received some >> push-back that my feeble in-driver attempts should be solved >> by the core. This is my attempt to do it right. >> >> I have obviously not tested all of this with more than a compile, >> but patches 1 through 5 are enough to make the atmel-hlcdc driver >> do what I need. The rest is just lots of removals and cleanup made >> possible by the improved core. >> >> Please test, I would not be surprised if I have fouled up some >> bit-manipulation somewhere, or if I have misunderstood something >> about atomics... >> >> Changes since v2: >> - Added patch 1/16 which factors out pseudo-palette handling. >> - Removed the if (cmap->start + cmap->len < cmap->start) >> sanity check on the assumption that the fbdev core handles it. >> - Added patch 4/16 which factors out atomic state and commit >> handling from drm_atomic_helper_legacy_gamma_set to >> drm_mode_gamma_set_ioctl. >> - Do one atomic commit for all affected crtc. >> - Removed a now obsolete note in include/drm/drm_crtc.h (ammended >> the last patch). >> - Cc list is getting long, so I have redused the list for the >> individual patches. If you would like to get the full series >> (or nothing at all) for the next round (if that is needed) just >> say so. > > Is this still on top of my locking rework? I tried to apply patches 1-3, > but there's minor conflicts ... > -Daniel v3 has the same base as v2. I collected your locking rework sometime after june 21, you have perhaps changed things since? I saw an update of that dpms patch you Cc me, but figured there were no significant changes that I needed to handle since I didn't get the full set this time either. A bad assumption it seems... Anyway, the base I have for v3 (and v2) is linux next-20170621 plus the following locking rework commits (in reverse order): Author: Thierry Reding <tred...@nvidia.com> Date: Wed Jun 21 20:28:15 2017 +0200 Subject: drm/hisilicon: Remove custom FB helper deferred setup Author: Thierry Reding <tred...@nvidia.com> Date: Wed Jun 21 20:28:14 2017 +0200 Subject: drm/exynos: Remove custom FB helper deferred setup Author: Thierry Reding <tred...@nvidia.com> Date: Wed Jun 21 20:28:13 2017 +0200 Subject: drm/fb-helper: Support deferred setup Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:12 2017 +0200 Subject: drm/fb-helper: Split dpms handling into legacy and atomic paths Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:11 2017 +0200 Subject: drm/fb-helper: Stop using mode_config.mutex for internals Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:10 2017 +0200 Subject: drm/fb-helper: Push locking into restore_fbdev_mode_atomic|legacy Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:09 2017 +0200 Subject: drm/fb-helper: Push locking into pan_display_atomic|legacy Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:08 2017 +0200 Subject: drm/fb-helper: Drop locking from the vsync wait ioctl code Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:07 2017 +0200 Subject: drm/fb-helper: Push locking in fb_is_bound Author: Thierry Reding <tred...@nvidia.com> Date: Wed Jun 21 20:28:06 2017 +0200 Subject: drm/fb-helper: Add top-level lock Author: Daniel Vetter <daniel.vet...@ffwll.ch> Date: Wed Jun 21 20:28:05 2017 +0200 Subject: drm/i915: Drop FBDEV #ifdev in mst code Author: Thierry Reding <tred...@nvidia.com> Date: Wed Jun 21 20:28:04 2017 +0200 Subject: drm/fb-helper: Push down modeset lock into FB helpers Cheers, peda
Re: [PATCH v3 05/16] drm/fb-helper: do a generic fb_setcmap helper in terms of crtc .gamma_set
On 2017-07-06 07:55, Daniel Vetter wrote: > On Wed, Jul 5, 2017 at 7:50 PM, Peter Rosin <p...@axentia.se> wrote: >>>> +retry: >>>> +ret = drm_modeset_lock_all_ctx(dev, ); >>> >>> With atomic you don't need to grab locks, this is done behind the scenes >>> (as long as you handle the retry/backoff correctly). See the kerneldoc for >>> the various drm_atomic_get_*_state functions. >> >> It doesn't work if I remove it. What is the disconnect? > > Good question, Duh, for symmetry I also removed the dropping of the locks. So the next call of course had no chance of getting access. How silly of me... >didn't spot this at first, but your backoff/retry logic > is proken. When typing drm_modeset_lock locking code please make sure > you've enabled both CONFIG_PROVE_LOCKING and > CONFIG_DEBUG_WW_MUTEX_SLOWPATH. Without these two it's really easy to > get this wrong. Please also read > https://dri.freedesktop.org/docs/drm/gpu/drm-kms.html#kms-locking > carefully plus all the kernel-doc of the various hooks. This stuff is > a really tricky locking scheme, it takes a while to understand it and > implement it correctly. Which is why all the locking magic is in > shared code and for normal drivers no need think about it. For the > fundamental algorithm, you can also check out the docs for w/w mutexes > at https://www.kernel.org/doc/Documentation/locking/ww-mutex-design.txt > > Might also help to read a bunch of the other locking paths again, with > my patches there's a few just in drm_fbdev_helper.c. I'll leave you > with these snippets here since I think this is fun to learn, but when > you're stuck I'm happy to help learn. I'll take a long look at this before I send a cleaned up v4. Thanks for the pointers... Cheers, peda
[PATCH v4 08/14] drm: gma500: remove dead code and pointless local lut storage
The redundant fb helpers .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/gma500/framebuffer.c | 22 drivers/gpu/drm/gma500/gma_display.c | 32 ++ drivers/gpu/drm/gma500/psb_intel_display.c | 7 +-- drivers/gpu/drm/gma500/psb_intel_drv.h | 1 - 4 files changed, 12 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 7da70b6..2570c7f 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -479,26 +479,6 @@ static struct drm_framebuffer *psb_user_framebuffer_create return psb_framebuffer_create(dev, cmd, r); } -static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - - gma_crtc->lut_r[regno] = red >> 8; - gma_crtc->lut_g[regno] = green >> 8; - gma_crtc->lut_b[regno] = blue >> 8; -} - -static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, int regno) -{ - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - - *red = gma_crtc->lut_r[regno] << 8; - *green = gma_crtc->lut_g[regno] << 8; - *blue = gma_crtc->lut_b[regno] << 8; -} - static int psbfb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { @@ -525,8 +505,6 @@ static int psbfb_probe(struct drm_fb_helper *helper, } static const struct drm_fb_helper_funcs psb_fb_helper_funcs = { - .gamma_set = psbfb_gamma_set, - .gamma_get = psbfb_gamma_get, .fb_probe = psbfb_probe, }; diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index e7fd356..f3c48a2 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -144,33 +144,32 @@ void gma_crtc_load_lut(struct drm_crtc *crtc) struct gma_crtc *gma_crtc = to_gma_crtc(crtc); const struct psb_offset *map = _priv->regmap[gma_crtc->pipe]; int palreg = map->palette; + u16 *r, *g, *b; int i; /* The clocks have to be on to load the palette. */ if (!crtc->enabled) return; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + if (gma_power_begin(dev, false)) { for (i = 0; i < 256; i++) { REG_WRITE(palreg + 4 * i, - ((gma_crtc->lut_r[i] + - gma_crtc->lut_adj[i]) << 16) | - ((gma_crtc->lut_g[i] + - gma_crtc->lut_adj[i]) << 8) | - (gma_crtc->lut_b[i] + - gma_crtc->lut_adj[i])); + (((*r++ >> 8) + gma_crtc->lut_adj[i]) << 16) | + (((*g++ >> 8) + gma_crtc->lut_adj[i]) << 8) | + ((*b++ >> 8) + gma_crtc->lut_adj[i])); } gma_power_end(dev); } else { for (i = 0; i < 256; i++) { /* FIXME: Why pipe[0] and not pipe[..._crtc->pipe]? */ dev_priv->regs.pipe[0].palette[i] = - ((gma_crtc->lut_r[i] + - gma_crtc->lut_adj[i]) << 16) | - ((gma_crtc->lut_g[i] + - gma_crtc->lut_adj[i]) << 8) | - (gma_crtc->lut_b[i] + - gma_crtc->lut_adj[i]); + (((*r++ >> 8) + gma_crtc->lut_adj[i]) << 16) | + (((*g++ >> 8) + gma_crtc->lut_adj[i]) << 8) | + ((*b++ >> 8) + gma_crtc->lut_adj[i]); } } @@ -180,15 +179,6 @@ int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, u32 size, struct drm_modeset_acquire_ctx *ctx) { - struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - gma_crtc->lut_r[i] = red[i] >> 8; - gma_crtc->lut_g[i] = green[i] >> 8; - gma_crtc->lut_b[i] = blue[i] >> 8; - } -
[PATCH v4 07/14] drm: cirrus: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/cirrus/cirrus_drv.h | 8 drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 - drivers/gpu/drm/cirrus/cirrus_mode.c | 71 --- 3 files changed, 16 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 8690352..be2d7e48 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -96,7 +96,6 @@ struct cirrus_crtc { struct drm_crtc base; - u8 lut_r[256], lut_g[256], lut_b[256]; int last_dpms; boolenabled; }; @@ -180,13 +179,6 @@ cirrus_bo(struct ttm_buffer_object *bo) #define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base) #define DRM_FILE_PAGE_OFFSET (0x1ULL >> PAGE_SHIFT) - /* cirrus_mode.c */ -void cirrus_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, int regno); -void cirrus_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, -u16 *blue, int regno); - - /* cirrus_main.c */ int cirrus_device_init(struct cirrus_device *cdev, struct drm_device *ddev, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 7fa58ee..1fedab0 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -265,8 +265,6 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, } static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { - .gamma_set = cirrus_crtc_fb_gamma_set, - .gamma_get = cirrus_crtc_fb_gamma_get, .fb_probe = cirrusfb_create, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 53f6f0f..a4c4a46 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -31,25 +31,6 @@ * This file contains setup code for the CRTC. */ -static void cirrus_crtc_load_lut(struct drm_crtc *crtc) -{ - struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct cirrus_device *cdev = dev->dev_private; - int i; - - if (!crtc->enabled) - return; - - for (i = 0; i < CIRRUS_LUT_SIZE; i++) { - /* VGA registers */ - WREG8(PALETTE_INDEX, i); - WREG8(PALETTE_DATA, cirrus_crtc->lut_r[i]); - WREG8(PALETTE_DATA, cirrus_crtc->lut_g[i]); - WREG8(PALETTE_DATA, cirrus_crtc->lut_b[i]); - } -} - /* * The DRM core requires DPMS functions, but they make little sense in our * case and so are just stubs @@ -330,15 +311,25 @@ static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_modeset_acquire_ctx *ctx) { - struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + u16 *r, *g, *b; int i; - for (i = 0; i < size; i++) { - cirrus_crtc->lut_r[i] = red[i]; - cirrus_crtc->lut_g[i] = green[i]; - cirrus_crtc->lut_b[i] = blue[i]; + if (!crtc->enabled) + return 0; + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + for (i = 0; i < CIRRUS_LUT_SIZE; i++) { + /* VGA registers */ + WREG8(PALETTE_INDEX, i); + WREG8(PALETTE_DATA, *r++ >> 8); + WREG8(PALETTE_DATA, *g++ >> 8); + WREG8(PALETTE_DATA, *b++ >> 8); } - cirrus_crtc_load_lut(crtc); return 0; } @@ -365,7 +356,6 @@ static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .mode_set_base = cirrus_crtc_mode_set_base, .prepare = cirrus_crtc_prepare, .commit = cirrus_crtc_commit, - .load_lut = cirrus_crtc_load_lut, }; /* CRTC setup */ @@ -373,7 +363,6 @@ static void cirrus_crtc_init(struct drm_device *dev) { struct cirrus_device *cdev = dev->dev_private; struct cirrus_crtc *cirrus_crtc; - int i; cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)), @@ -387,37 +376,9 @@ static void cirrus_crtc_init(st
[PATCH v4 10/14] drm: mgag200: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/mgag200/mgag200_drv.h | 5 --- drivers/gpu/drm/mgag200/mgag200_fb.c | 2 -- drivers/gpu/drm/mgag200/mgag200_mode.c | 62 -- 3 files changed, 15 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index c88b6ec..04f1dfb 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -237,11 +237,6 @@ mgag200_bo(struct ttm_buffer_object *bo) { return container_of(bo, struct mgag200_bo, bo); } - /* mgag200_crtc.c */ -void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, int regno); -void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, -u16 *blue, int regno); /* mgag200_mode.c */ int mgag200_modeset_init(struct mga_device *mdev); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5d3b1fa..5cf980a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -258,8 +258,6 @@ static int mga_fbdev_destroy(struct drm_device *dev, } static const struct drm_fb_helper_funcs mga_fb_helper_funcs = { - .gamma_set = mga_crtc_fb_gamma_set, - .gamma_get = mga_crtc_fb_gamma_get, .fb_probe = mgag200fb_create, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index f4b5358..5e9cd4c 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -27,15 +27,19 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc) { - struct mga_crtc *mga_crtc = to_mga_crtc(crtc); struct drm_device *dev = crtc->dev; struct mga_device *mdev = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; + u16 *r_ptr, *g_ptr, *b_ptr; int i; if (!crtc->enabled) return; + r_ptr = crtc->gamma_store; + g_ptr = r_ptr + crtc->gamma_size; + b_ptr = g_ptr + crtc->gamma_size; + WREG8(DAC_INDEX + MGA1064_INDEX, 0); if (fb && fb->format->cpp[0] * 8 == 16) { @@ -46,25 +50,27 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc) if (i > (MGAG200_LUT_SIZE >> 1)) { r = b = 0; } else { - r = mga_crtc->lut_r[i << 1]; - b = mga_crtc->lut_b[i << 1]; + r = *r_ptr++ >> 8; + b = *b_ptr++ >> 8; + r_ptr++; + b_ptr++; } } else { - r = mga_crtc->lut_r[i]; - b = mga_crtc->lut_b[i]; + r = *r_ptr++ >> 8; + b = *b_ptr++ >> 8; } /* VGA registers */ WREG8(DAC_INDEX + MGA1064_COL_PAL, r); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); WREG8(DAC_INDEX + MGA1064_COL_PAL, b); } return; } for (i = 0; i < MGAG200_LUT_SIZE; i++) { /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_r[i]); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); - WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_b[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8); } } @@ -1399,14 +1405,6 @@ static int mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_modeset_acquire_ctx *ctx) { - struct mga_crtc *mga_crtc = to_mga_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - mga_crtc->lut_r[i] = red[i] >> 8; - mga_crtc->lut_g[i] = green[i] >> 8; - mga_crtc->lut_b[i] = blue[i] >> 8; -
[PATCH v4 14/14] drm: remove unused and redundant callbacks
Drivers no longer have any need for these callbacks, and there are no users. Zap. Zap-zap-zzzap-p-pp-p. Signed-off-by: Peter Rosin <p...@axentia.se> --- include/drm/drm_crtc.h | 8 include/drm/drm_fb_helper.h | 32 include/drm/drm_modeset_helper_vtables.h | 16 3 files changed, 56 deletions(-) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3a911a6..0cc8962 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -358,14 +358,6 @@ struct drm_crtc_funcs { * drm_crtc_enable_color_mgmt(), which then supports the legacy gamma * interface through the drm_atomic_helper_legacy_gamma_set() * compatibility implementation. -* -* NOTE: -* -* Drivers that support gamma tables and also fbdev emulation through -* the provided helper library need to take care to fill out the gamma -* hooks for both. Currently there's a bit an unfortunate duplication -* going on, which should eventually be unified to just one set of -* hooks. */ int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size, diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index ea170b9..21c5630 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -85,38 +85,6 @@ struct drm_fb_helper_surface_size { */ struct drm_fb_helper_funcs { /** -* @gamma_set: -* -* Set the given gamma LUT register on the given CRTC. -* -* This callback is optional. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno); - /** -* @gamma_get: -* -* Read the given gamma LUT register on the given CRTC, used to save the -* current LUT when force-restoring the fbdev for e.g. kdbg. -* -* This callback is optional. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno); - - /** * @fb_probe: * * Driver callback to allocate and initialize the fbdev info structure. diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 0656984..6cdcb42 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -267,22 +267,6 @@ struct drm_crtc_helper_funcs { enum mode_set_atomic); /** -* @load_lut: -* -* Load a LUT prepared with the _fb_helper_funcs.gamma_set vfunc. -* -* This callback is optional and is only used by the fbdev emulation -* helpers. -* -* FIXME: -* -* This callback is functionally redundant with the core gamma table -* support and simply exists because the fbdev hasn't yet been -* refactored to use the core gamma table interfaces. -*/ - void (*load_lut)(struct drm_crtc *crtc); - - /** * @disable: * * This callback should be used to disable the CRTC. With the atomic -- 2.1.4
[PATCH v4 12/14] drm: radeon: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/radeon/atombios_crtc.c | 1 - drivers/gpu/drm/radeon/radeon_connectors.c | 7 ++- drivers/gpu/drm/radeon/radeon_display.c | 71 - drivers/gpu/drm/radeon/radeon_fb.c | 2 - drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 1 - drivers/gpu/drm/radeon/radeon_mode.h| 4 -- 6 files changed, 33 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 3c492a0..02baaaf 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -2217,7 +2217,6 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = { .mode_set_base_atomic = atombios_crtc_set_base_atomic, .prepare = atombios_crtc_prepare, .commit = atombios_crtc_commit, - .load_lut = radeon_crtc_load_lut, .disable = atombios_crtc_disable, }; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 27affbd..2f642cb 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -773,12 +773,15 @@ static int radeon_connector_set_property(struct drm_connector *connector, struct if (connector->encoder->crtc) { struct drm_crtc *crtc = connector->encoder->crtc; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); radeon_crtc->output_csc = radeon_encoder->output_csc; - (*crtc_funcs->load_lut)(crtc); + /* +* Our .gamma_set assumes the .gamma_store has been +* prefilled and don't care about its arguments. +*/ + crtc->funcs->gamma_set(crtc, NULL, NULL, NULL, 0, NULL); } } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 17d3daf..8b7d7a0 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -42,6 +42,7 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; + u16 *r, *g, *b; int i; DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); @@ -60,11 +61,14 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc) WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x003f); WREG8(AVIVO_DC_LUT_RW_INDEX, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(AVIVO_DC_LUT_30_COLOR, -(radeon_crtc->lut_r[i] << 20) | -(radeon_crtc->lut_g[i] << 10) | -(radeon_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } /* Only change bit 0 of LUT_SEL, other bits are set elsewhere */ @@ -76,6 +80,7 @@ static void dce4_crtc_load_lut(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; + u16 *r, *g, *b; int i; DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); @@ -93,11 +98,14 @@ static void dce4_crtc_load_lut(struct drm_crtc *crtc) WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x0007); WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, - (radeon_crtc->lut_r[i] << 20) | - (radeon_crtc->lut_g[i] << 10) | - (radeon_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } } @@ -106,6 +114,7 @@ static void dce5_crtc_load_lut(struct drm_crtc *crtc) struct rad
[PATCH v4 13/14] drm: stm: remove dead code and pointless local lut storage
The redundant fb helper .load_lut is no longer used, and can not work right without also providing the fb helpers .gamma_set and .gamma_get thus rendering the code in this driver suspect. Just remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/stm/ltdc.c | 12 drivers/gpu/drm/stm/ltdc.h | 1 - 2 files changed, 13 deletions(-) diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 5331760..3e95b4d 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -375,17 +375,6 @@ static irqreturn_t ltdc_irq(int irq, void *arg) * DRM_CRTC */ -static void ltdc_crtc_load_lut(struct drm_crtc *crtc) -{ - struct ltdc_device *ldev = crtc_to_ltdc(crtc); - unsigned int i, lay; - - for (lay = 0; lay < ldev->caps.nb_layers; lay++) - for (i = 0; i < 256; i++) - reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS, - ldev->clut[i]); -} - static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -525,7 +514,6 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, } static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { - .load_lut = ltdc_crtc_load_lut, .mode_set_nofb = ltdc_crtc_mode_set_nofb, .atomic_flush = ltdc_crtc_atomic_flush, .atomic_enable = ltdc_crtc_atomic_enable, diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index d7a9c73..620ca55 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -27,7 +27,6 @@ struct ltdc_device { struct drm_panel *panel; struct mutex err_lock; /* protecting error_status */ struct ltdc_caps caps; - u32 clut[256]; /* color look up table */ u32 error_status; u32 irq_status; }; -- 2.1.4
[PATCH v4 03/14] drm/fb-helper: separate the fb_setcmap helper into atomic and legacy paths
The legacy path implements setcmap in terms of crtc .gamma_set. The atomic path implements setcmap by directly updating the crtc gamma_lut property. This has a couple of benefits: - it makes the redundant fb helpers .load_lut, .gamma_set and .gamma_get completely obsolete. They are now unused and subject for removal. - atomic drivers that support clut modes get fbdev support for those from the drm core. This includes atmel-hlcdc, but perhaps others as well? Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_fb_helper.c | 232 1 file changed, 161 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 721511d..32d6ea1 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1195,27 +1195,6 @@ void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); -static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, -u16 blue, u16 regno, struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_framebuffer *fb = fb_helper->fb; - - /* -* The driver really shouldn't advertise pseudo/directcolor -* visuals if it can't deal with the palette. -*/ - if (WARN_ON(!fb_helper->funcs->gamma_set || - !fb_helper->funcs->gamma_get)) - return -EINVAL; - - WARN_ON(fb->format->cpp[0] != 1); - - fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); - - return 0; -} - static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) { u32 *palette = (u32 *)info->pseudo_palette; @@ -1248,57 +1227,140 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) return 0; } -/** - * drm_fb_helper_setcmap - implementation for _ops.fb_setcmap - * @cmap: cmap to set - * @info: fbdev registered by the helper - */ -int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) +static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - const struct drm_crtc_helper_funcs *crtc_funcs; - u16 *red, *green, *blue, *transp; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, j, rc = 0; - int start; + int i, ret = 0; - if (oops_in_progress) - return -EBUSY; + drm_modeset_lock_all(fb_helper->dev); + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + if (!crtc->funcs->gamma_set || !crtc->gamma_size) + return -EINVAL; - mutex_lock(_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { - mutex_unlock(_helper->lock); - return -EBUSY; + if (cmap->start + cmap->len > crtc->gamma_size) + return -EINVAL; + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); + memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); + memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); + + ret = crtc->funcs->gamma_set(crtc, r, g, b, +crtc->gamma_size, NULL); + if (ret) + return ret; } + drm_modeset_unlock_all(fb_helper->dev); - drm_modeset_lock_all(dev); - if (info->fix.visual == FB_VISUAL_TRUECOLOR) { - rc = setcmap_pseudo_palette(cmap, info); - goto out; + return ret; +} + +static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc, + struct fb_cmap *cmap) +{ + struct drm_device *dev = crtc->dev; + struct drm_property_blob *gamma_lut; + struct drm_color_lut *lut; + int size = crtc->gamma_size; + int i; + + if (!size || cmap->start + cmap->len > size) + return ERR_PTR(-EINVAL); + + gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL); + if (IS_ERR(gamma_lut)) + return gamma_lut; + + lut = (struct drm_color_lut *)gamma_lut->data; + if (cmap->start || cmap->len != size) { + u16 *r = crtc->gamma_store; + u16 *g = r + crtc->gamma_size; + u16 *b = g + crtc->gamma_size; + + for (i = 0; i < cmap->start; i++) { + lut
[PATCH v4 04/14] drm: amd: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 24 drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 1 - drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v6_0.c| 27 +++ drivers/gpu/drm/amd/amdgpu/dce_v8_0.c| 27 +++ drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 23 --- 7 files changed, 28 insertions(+), 128 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index c0d8c6f..7dc3780 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -312,31 +312,7 @@ static int amdgpu_fbdev_destroy(struct drm_device *dev, struct amdgpu_fbdev *rfb return 0; } -/** Sets the color ramps on behalf of fbcon */ -static void amdgpu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - - amdgpu_crtc->lut_r[regno] = red >> 6; - amdgpu_crtc->lut_g[regno] = green >> 6; - amdgpu_crtc->lut_b[regno] = blue >> 6; -} - -/** Gets the color ramps on behalf of fbcon */ -static void amdgpu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - - *red = amdgpu_crtc->lut_r[regno] << 6; - *green = amdgpu_crtc->lut_g[regno] << 6; - *blue = amdgpu_crtc->lut_b[regno] << 6; -} - static const struct drm_fb_helper_funcs amdgpu_fb_helper_funcs = { - .gamma_set = amdgpu_crtc_fb_gamma_set, - .gamma_get = amdgpu_crtc_fb_gamma_get, .fb_probe = amdgpufb_create, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 43a9d3a..39f7eda 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -369,7 +369,6 @@ struct amdgpu_atom_ss { struct amdgpu_crtc { struct drm_crtc base; int crtc_id; - u16 lut_r[256], lut_g[256], lut_b[256]; bool enabled; bool can_tile; uint32_t crtc_offset; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 9f78c03..c958023 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2267,6 +2267,7 @@ static void dce_v10_0_crtc_load_lut(struct drm_crtc *crtc) struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); struct drm_device *dev = crtc->dev; struct amdgpu_device *adev = dev->dev_private; + u16 *r, *g, *b; int i; u32 tmp; @@ -2304,11 +2305,14 @@ static void dce_v10_0_crtc_load_lut(struct drm_crtc *crtc) WREG32(mmDC_LUT_WRITE_EN_MASK + amdgpu_crtc->crtc_offset, 0x0007); WREG32(mmDC_LUT_RW_INDEX + amdgpu_crtc->crtc_offset, 0); + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; for (i = 0; i < 256; i++) { WREG32(mmDC_LUT_30_COLOR + amdgpu_crtc->crtc_offset, - (amdgpu_crtc->lut_r[i] << 20) | - (amdgpu_crtc->lut_g[i] << 10) | - (amdgpu_crtc->lut_b[i] << 0)); + ((*r++ & 0xffc0) << 14) | + ((*g++ & 0xffc0) << 4) | + (*b++ >> 6)); } tmp = RREG32(mmDEGAMMA_CONTROL + amdgpu_crtc->crtc_offset); @@ -2624,15 +2628,6 @@ static int dce_v10_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_modeset_acquire_ctx *ctx) { - struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); - int i; - - /* userspace palettes are always correct as is */ - for (i = 0; i < size; i++) { - amdgpu_crtc->lut_r[i] = red[i] >> 6; - amdgpu_crtc->lut_g[i] = green[i] >> 6; - amdgpu_crtc->lut_b[i] = blue[i] >> 6; - } dce_v10_0_crtc_load_lut(crtc); return 0; @@ -2844,14 +2839,12 @@ static const struct drm_crtc_helper_funcs dce_v10_0_crtc_helper_funcs = { .mode_set_base_atomic = dce_v10_0_crtc_set_base_atomic, .prepare = dce_v10_0_crtc_prepare,
[PATCH v4 00/14] improve the fb_setcmap helper
Hi! While trying to get CLUT support for the atmel_hlcdc driver, and specifically for the emulated fbdev interface, I received some push-back that my feeble in-driver attempts should be solved by the core. This is my attempt to do it right. I have obviously not tested all of this with more than a compile, but patches 1 and 3 are enough to make the atmel-hlcdc driver do what I need. The rest is just lots of removals and cleanup made possible by the other improvements. Please test, I would not be surprised if I have fouled up some bit-manipulation somewhere, or if I have misunderstood something about atomics... Changes since v3: - Rebased onto drm-misc-next and dropped patches 1-3 from v3, since they are already merged. - Dropped the v3 patch 4/16 ("drm/color-mgmt: move atomic state/commit out from .gamma_set") since the atomic setcmap no longer uses the crtc .gamma_set callback. - Added patch 1/14 which exports drm_atomic_replace_property_blob... - ...and patch 2/14 which uses this new export to simplify drm_atomic_helper_legacy_gamma_set. - Big changes to patch 3/14 (was 5/16 in v3). It had various locking issues and the atomic setcmap is rather different. Changes since v2: - Added patch 1/16 which factors out pseudo-palette handling. - Removed the if (cmap->start + cmap->len < cmap->start) sanity check on the assumption that the fbdev core handles it. - Added patch 4/16 which factors out atomic state and commit handling from drm_atomic_helper_legacy_gamma_set to drm_mode_gamma_set_ioctl. - Do one atomic commit for all affected crtc. - Removed a now obsolete note in include/drm/drm_crtc.h (ammended the last patch). - Cc list is getting long, so I have redused the list for the individual patches. If you would like to get the full series (or nothing at all) for the next round (if that is needed) just say so. Changes since v1: - Rebased to next-20170621 - Split 1/11 into a preparatory patch, a cleanup patch and then the meat in 3/14. - Handle pseudo-palette for FB_VISUAL_TRUECOLOR. - Removed the empty .gamma_get/.gamma_set fb helpers from the armada driver that I had somehow managed to ignore but which 0day found real quick. - Be less judgemental on drivers only providing .gamma_get and .gamma_set, but no .load_lut. That's actually a valid thing to do if you only need pseudo-palette for FB_VISUAL_TRUECOLOR. - Add a comment about colliding bitfields in the nouveau driver. - Remove gamma_set/gamma_get declarations from the radeon driver (the definitions were removed in v1). Cheers, peda Peter Rosin (14): drm/atomic: export drm_atomic_replace_property_blob drm/atomic-helper: update lut props directly in ..._legacy_gamma_set drm/fb-helper: separate the fb_setcmap helper into atomic and legacy paths drm: amd: remove dead code and pointless local lut storage drm: armada: remove dead empty functions drm: ast: remove dead code and pointless local lut storage drm: cirrus: remove dead code and pointless local lut storage drm: gma500: remove dead code and pointless local lut storage drm: i915: remove dead code and pointless local lut storage drm: mgag200: remove dead code and pointless local lut storage drm: nouveau: remove dead code and pointless local lut storage drm: radeon: remove dead code and pointless local lut storage drm: stm: remove dead code and pointless local lut storage drm: remove unused and redundant callbacks drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 24 --- drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h| 1 - drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 27 +--- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 27 +--- drivers/gpu/drm/amd/amdgpu/dce_v6_0.c | 27 +--- drivers/gpu/drm/amd/amdgpu/dce_v8_0.c | 27 +--- drivers/gpu/drm/amd/amdgpu/dce_virtual.c| 23 --- drivers/gpu/drm/armada/armada_crtc.c| 10 -- drivers/gpu/drm/armada/armada_crtc.h| 2 - drivers/gpu/drm/armada/armada_fbdev.c | 2 - drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_fb.c| 20 --- drivers/gpu/drm/ast/ast_mode.c | 26 +--- drivers/gpu/drm/cirrus/cirrus_drv.h | 8 - drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 - drivers/gpu/drm/cirrus/cirrus_mode.c| 71 ++--- drivers/gpu/drm/drm_atomic.c| 17 +- drivers/gpu/drm/drm_atomic_helper.c | 23 +-- drivers/gpu/drm/drm_fb_helper.c | 232 +++- drivers/gpu/drm/gma500/framebuffer.c| 22 --- drivers/gpu/drm/gma500/gma_display.c| 32 ++-- drivers/gpu/drm/gma500/psb_intel_display.c | 7 +- drivers/gpu/drm/gma500/psb_intel_drv.h | 1 - drivers/gpu/drm/i915/intel_drv.h| 1 - drivers/gpu/drm/i915/intel_fbdev.c | 31 drivers/gpu/drm/mgag200/mgag200_drv.h | 5 - drivers/gpu/drm/mgag200/mgag200_fb.c| 2 - drivers/gpu/drm/
[PATCH v4 06/14] drm: ast: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_fb.c | 20 drivers/gpu/drm/ast/ast_mode.c | 26 ++ 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 8880f0b..569a148 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -245,7 +245,6 @@ struct ast_connector { struct ast_crtc { struct drm_crtc base; - u8 lut_r[256], lut_g[256], lut_b[256]; struct drm_gem_object *cursor_bo; uint64_t cursor_addr; int cursor_width, cursor_height; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 4ad4acd..dbabcac 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -255,27 +255,7 @@ static int astfb_create(struct drm_fb_helper *helper, return ret; } -static void ast_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - ast_crtc->lut_r[regno] = red >> 8; - ast_crtc->lut_g[regno] = green >> 8; - ast_crtc->lut_b[regno] = blue >> 8; -} - -static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - *red = ast_crtc->lut_r[regno] << 8; - *green = ast_crtc->lut_g[regno] << 8; - *blue = ast_crtc->lut_b[regno] << 8; -} - static const struct drm_fb_helper_funcs ast_fb_helper_funcs = { - .gamma_set = ast_fb_gamma_set, - .gamma_get = ast_fb_gamma_get, .fb_probe = astfb_create, }; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index aaef0a6..724c16b 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -63,15 +63,18 @@ static inline void ast_load_palette_index(struct ast_private *ast, static void ast_crtc_load_lut(struct drm_crtc *crtc) { struct ast_private *ast = crtc->dev->dev_private; - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + u16 *r, *g, *b; int i; if (!crtc->enabled) return; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + for (i = 0; i < 256; i++) - ast_load_palette_index(ast, i, ast_crtc->lut_r[i], - ast_crtc->lut_g[i], ast_crtc->lut_b[i]); + ast_load_palette_index(ast, i, *r++ >> 8, *g++ >> 8, *b++ >> 8); } static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mode *mode, @@ -633,7 +636,6 @@ static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { .mode_set = ast_crtc_mode_set, .mode_set_base = ast_crtc_mode_set_base, .disable = ast_crtc_disable, - .load_lut = ast_crtc_load_lut, .prepare = ast_crtc_prepare, .commit = ast_crtc_commit, @@ -648,15 +650,6 @@ static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size, struct drm_modeset_acquire_ctx *ctx) { - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - int i; - - /* userspace palettes are always correct as is */ - for (i = 0; i < size; i++) { - ast_crtc->lut_r[i] = red[i] >> 8; - ast_crtc->lut_g[i] = green[i] >> 8; - ast_crtc->lut_b[i] = blue[i] >> 8; - } ast_crtc_load_lut(crtc); return 0; @@ -681,7 +674,6 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { static int ast_crtc_init(struct drm_device *dev) { struct ast_crtc *crtc; - int i; crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL); if (!crtc) @@ -690,12 +682,6 @@ static int ast_crtc_init(struct drm_device *dev) drm_crtc_init(dev, >base, _crtc_funcs); drm_mode_crtc_set_gamma_size(>base, 256); drm_crtc_helper_add(>base, _crtc_helper_funcs); - - for (i = 0; i < 256; i++) { - crtc->lut_r[i] = i; - crtc->lut_g[i] = i; - crtc->lut_b[i] = i; - } return 0; } -- 2.1.4
[PATCH v4 11/14] drm: nouveau: remove dead code and pointless local lut storage
The redundant fb helpers .load_lut, .gamma_set and .gamma_get are no longer used. Remove the dead code and hook up the crtc .gamma_set to use the crtc gamma_store directly instead of duplicating that info locally. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 26 - drivers/gpu/drm/nouveau/nouveau_crtc.h | 3 --- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 22 -- drivers/gpu/drm/nouveau/nv50_display.c | 40 +++-- 4 files changed, 22 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 4b4b0b4..8f689f1 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -764,13 +764,18 @@ nv_crtc_gamma_load(struct drm_crtc *crtc) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct drm_device *dev = nv_crtc->base.dev; struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; + u16 *r, *g, *b; int i; rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + for (i = 0; i < 256; i++) { - rgbs[i].r = nv_crtc->lut.r[i] >> 8; - rgbs[i].g = nv_crtc->lut.g[i] >> 8; - rgbs[i].b = nv_crtc->lut.b[i] >> 8; + rgbs[i].r = *r++ >> 8; + rgbs[i].g = *g++ >> 8; + rgbs[i].b = *b++ >> 8; } nouveau_hw_load_state_palette(dev, nv_crtc->index, _display(dev)->mode_reg); @@ -792,13 +797,6 @@ nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, struct drm_modeset_acquire_ctx *ctx) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int i; - - for (i = 0; i < size; i++) { - nv_crtc->lut.r[i] = r[i]; - nv_crtc->lut.g[i] = g[i]; - nv_crtc->lut.b[i] = b[i]; - } /* We need to know the depth before we upload, but it's possible to * get called before a framebuffer is bound. If this is the case, @@ -1095,7 +1093,6 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { .mode_set = nv_crtc_mode_set, .mode_set_base = nv04_crtc_mode_set_base, .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, - .load_lut = nv_crtc_gamma_load, .disable = nv_crtc_disable, }; @@ -1103,17 +1100,12 @@ int nv04_crtc_create(struct drm_device *dev, int crtc_num) { struct nouveau_crtc *nv_crtc; - int ret, i; + int ret; nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); if (!nv_crtc) return -ENOMEM; - for (i = 0; i < 256; i++) { - nv_crtc->lut.r[i] = i << 8; - nv_crtc->lut.g[i] = i << 8; - nv_crtc->lut.b[i] = i << 8; - } nv_crtc->lut.depth = 0; nv_crtc->index = crtc_num; diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index 050fcf3..b7a18fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -61,9 +61,6 @@ struct nouveau_crtc { struct { struct nouveau_bo *nvbo; - uint16_t r[256]; - uint16_t g[256]; - uint16_t b[256]; int depth; } lut; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 2665a07..f770784 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -278,26 +278,6 @@ nouveau_fbcon_accel_init(struct drm_device *dev) info->fbops = _fbcon_ops; } -static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - nv_crtc->lut.r[regno] = red; - nv_crtc->lut.g[regno] = green; - nv_crtc->lut.b[regno] = blue; -} - -static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - *red = nv_crtc->lut.r[regno]; - *green = nv_crtc->lut.g[regno]; - *blue = nv_crtc->lut.b[regno]; -} - static void nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) { @@ -467,8 +447,6 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) } static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { - .gamma_set = nouveau_fbcon_gamma_set, - .gamma_get = nouveau_fbcon_gamma_get,
[PATCH v4 01/14] drm/atomic: export drm_atomic_replace_property_blob
While at it, add some words in the kernel-doc about the 'replaced' arg and remove a faulty kernel-doc comment on the return value. Also remove a redundant return statement. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_atomic.c | 17 + include/drm/drm_atomic.h | 4 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 09ca662..b7d9696 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -414,13 +414,15 @@ EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); * @new_blob: the new blob to replace with * @replaced: whether the blob has been replaced * - * RETURNS: - * Zero on success, error code on failure + * Note that you are required to initialize @replaced to false before the + * call, since it is only set to true when the blob property is changed and + * not set to false when the property is not changed. This enables a series + * of calls to be made where you are interested in if any property is + * replaced, but not care so much about exactly which of them was replaced. */ -static void -drm_atomic_replace_property_blob(struct drm_property_blob **blob, -struct drm_property_blob *new_blob, -bool *replaced) +void drm_atomic_replace_property_blob(struct drm_property_blob **blob, + struct drm_property_blob *new_blob, + bool *replaced) { struct drm_property_blob *old_blob = *blob; @@ -432,9 +434,8 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob, drm_property_blob_get(new_blob); *blob = new_blob; *replaced = true; - - return; } +EXPORT_SYMBOL(drm_atomic_replace_property_blob); static int drm_atomic_replace_property_blob_from_id(struct drm_device *dev, diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index dcc8e0c..8b32ea5 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -321,6 +321,10 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, uint64_t val); +void drm_atomic_replace_property_blob(struct drm_property_blob **blob, + struct drm_property_blob *new_blob, + bool *replaced); + void * __must_check drm_atomic_get_private_obj_state(struct drm_atomic_state *state, void *obj, -- 2.1.4
[PATCH v4 02/14] drm/atomic-helper: update lut props directly in ..._legacy_gamma_set
Do not waste cycles looking up the property id when we have the actual property already. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/drm_atomic_helper.c | 23 --- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 667ec97..5a4a344 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3769,11 +3769,11 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; - struct drm_mode_config *config = >mode_config; struct drm_atomic_state *state; struct drm_crtc_state *crtc_state; struct drm_property_blob *blob = NULL; struct drm_color_lut *blob_data; + bool replaced = false; int i, ret = 0; state = drm_atomic_state_alloc(crtc->dev); @@ -3805,20 +3805,13 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, } /* Reset DEGAMMA_LUT and CTM properties. */ - ret = drm_atomic_crtc_set_property(crtc, crtc_state, - config->degamma_lut_property, 0); - if (ret) - goto fail; - - ret = drm_atomic_crtc_set_property(crtc, crtc_state, - config->ctm_property, 0); - if (ret) - goto fail; - - ret = drm_atomic_crtc_set_property(crtc, crtc_state, - config->gamma_lut_property, blob->base.id); - if (ret) - goto fail; + drm_atomic_replace_property_blob(_state->degamma_lut, +NULL, ); + drm_atomic_replace_property_blob(_state->ctm, +NULL, ); + drm_atomic_replace_property_blob(_state->gamma_lut, +blob, ); + crtc_state->color_mgmt_changed |= replaced; ret = drm_atomic_commit(state); -- 2.1.4
[PATCH v4 05/14] drm: armada: remove dead empty functions
The redundant fb helpers .gamma_set and .gamma_get are no longer used. Remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/armada/armada_crtc.c | 10 -- drivers/gpu/drm/armada/armada_crtc.h | 2 -- drivers/gpu/drm/armada/armada_fbdev.c | 2 -- 3 files changed, 14 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index b57fb80..5d5cc32 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -334,16 +334,6 @@ static void armada_drm_vblank_off(struct armada_crtc *dcrtc) armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); } -void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, - int idx) -{ -} - -void armada_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - int idx) -{ -} - /* The mode_config.mutex will be held for this call */ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) { diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 7e8906d..bab11f4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -102,8 +102,6 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); -void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 602dfea..5fa076d 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -118,8 +118,6 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, } static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { - .gamma_set = armada_drm_crtc_gamma_set, - .gamma_get = armada_drm_crtc_gamma_get, .fb_probe = armada_fb_probe, }; -- 2.1.4
[PATCH v4 09/14] drm: i915: remove dead code and pointless local lut storage
The driver stores lut values from the fbdev interface, and is able to give them back, but does not appear to do anything with these lut values. The generic fb helpers have replaced this function, and may even have made the driver work for the C8 mode from the fbdev interface. But that is untested. Since the fb helpers .gamma_set and .gamma_get are obsolete, remove the dead code. Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/gpu/drm/i915/intel_drv.h | 1 - drivers/gpu/drm/i915/intel_fbdev.c | 31 --- 2 files changed, 32 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d93efb4..bc7bfa0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -786,7 +786,6 @@ struct intel_crtc { struct drm_crtc base; enum pipe pipe; enum plane plane; - u8 lut_r[256], lut_g[256], lut_b[256]; /* * Whether the crtc and the connected output pipeline is active. Implies * that crtc->enabled is set, i.e. the current mode configuration has diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 460ca0b..19d650b 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -281,27 +281,6 @@ static int intelfb_create(struct drm_fb_helper *helper, return ret; } -/** Sets the color ramps on behalf of RandR */ -static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - intel_crtc->lut_r[regno] = red >> 8; - intel_crtc->lut_g[regno] = green >> 8; - intel_crtc->lut_b[regno] = blue >> 8; -} - -static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - *red = intel_crtc->lut_r[regno] << 8; - *green = intel_crtc->lut_g[regno] << 8; - *blue = intel_crtc->lut_b[regno] << 8; -} - static struct drm_fb_helper_crtc * intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) { @@ -376,7 +355,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, struct drm_connector *connector; struct drm_encoder *encoder; struct drm_fb_helper_crtc *new_crtc; - struct intel_crtc *intel_crtc; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; @@ -418,13 +396,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, num_connectors_enabled++; - intel_crtc = to_intel_crtc(connector->state->crtc); - for (j = 0; j < 256; j++) { - intel_crtc->lut_r[j] = j; - intel_crtc->lut_g[j] = j; - intel_crtc->lut_b[j] = j; - } - new_crtc = intel_fb_helper_crtc(fb_helper, connector->state->crtc); @@ -527,8 +498,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { .initial_config = intel_fb_initial_config, - .gamma_set = intel_crtc_fb_gamma_set, - .gamma_get = intel_crtc_fb_gamma_get, .fb_probe = intelfb_create, }; -- 2.1.4
Re: [PATCH v3 05/16] drm/fb-helper: do a generic fb_setcmap helper in terms of crtc .gamma_set
On 2017-07-05 08:21, Daniel Vetter wrote: > On Tue, Jul 04, 2017 at 12:37:01PM +0200, Peter Rosin wrote: >> This makes the redundant fb helpers .load_lut, .gamma_set and .gamma_get >> completely obsolete. >> >> Signed-off-by: Peter Rosin <p...@axentia.se> >> --- >> drivers/gpu/drm/drm_fb_helper.c | 165 >> +++- >> 1 file changed, 94 insertions(+), 71 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_fb_helper.c >> b/drivers/gpu/drm/drm_fb_helper.c >> index b75b1f2..7f8199a 100644 >> --- a/drivers/gpu/drm/drm_fb_helper.c >> +++ b/drivers/gpu/drm/drm_fb_helper.c >> @@ -1257,27 +1257,6 @@ void drm_fb_helper_set_suspend_unlocked(struct >> drm_fb_helper *fb_helper, >> } >> EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); >> >> -static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, >> - u16 blue, u16 regno, struct fb_info *info) >> -{ >> -struct drm_fb_helper *fb_helper = info->par; >> -struct drm_framebuffer *fb = fb_helper->fb; >> - >> -/* >> - * The driver really shouldn't advertise pseudo/directcolor >> - * visuals if it can't deal with the palette. >> - */ >> -if (WARN_ON(!fb_helper->funcs->gamma_set || >> -!fb_helper->funcs->gamma_get)) >> -return -EINVAL; >> - >> -WARN_ON(fb->format->cpp[0] != 1); >> - >> -fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); >> - >> -return 0; >> -} >> - >> static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info >> *info) >> { >> u32 *palette = (u32 *)info->pseudo_palette; >> @@ -1310,54 +1289,68 @@ static int setcmap_pseudo_palette(struct fb_cmap >> *cmap, struct fb_info *info) >> return 0; >> } >> >> -/** >> - * drm_fb_helper_setcmap - implementation for _ops.fb_setcmap >> - * @cmap: cmap to set >> - * @info: fbdev registered by the helper >> - */ >> -int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) >> +static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) >> { >> struct drm_fb_helper *fb_helper = info->par; >> -struct drm_device *dev = fb_helper->dev; >> -const struct drm_crtc_helper_funcs *crtc_funcs; >> -u16 *red, *green, *blue, *transp; >> struct drm_crtc *crtc; >> u16 *r, *g, *b; >> -int i, j, rc = 0; >> -int start; >> +int i, ret = 0; >> >> -if (oops_in_progress) >> -return -EBUSY; >> +for (i = 0; i < fb_helper->crtc_count; i++) { >> +crtc = fb_helper->crtc_info[i].mode_set.crtc; >> +if (!crtc->funcs->gamma_set || !crtc->gamma_size) >> +return -EINVAL; >> >> -mutex_lock(_helper->lock); >> -if (!drm_fb_helper_is_bound(fb_helper)) { >> -mutex_unlock(_helper->lock); >> -return -EBUSY; >> -} >> +if (cmap->start + cmap->len > crtc->gamma_size) >> +return -EINVAL; >> >> -drm_modeset_lock_all(dev); >> -if (info->fix.visual == FB_VISUAL_TRUECOLOR) { >> -rc = setcmap_pseudo_palette(cmap, info); >> -goto out; >> +r = crtc->gamma_store; >> +g = r + crtc->gamma_size; >> +b = g + crtc->gamma_size; >> + >> +memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); >> +memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); >> +memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); >> + >> +ret = crtc->funcs->gamma_set(crtc, r, g, b, >> + crtc->gamma_size, NULL); >> +if (ret) >> +return ret; >> } >> >> -for (i = 0; i < fb_helper->crtc_count; i++) { >> -crtc = fb_helper->crtc_info[i].mode_set.crtc; >> -crtc_funcs = crtc->helper_private; >> +return ret; >> +} > > For the legacy path you need to keep the drm_modeset_lock_all (but only in > setcmap_legacy). Otherwise this part here looks good. Oops, didn't intend to zap that one. Thanks for catching! >> >> -red = cmap->red; >> -green = cmap->green; >> -blue = cmap-&g
Re: [PATCH] iio: multiplexer: add NULL check on devm_kzalloc() return value
On 2017-07-07 00:08, Gustavo A. R. Silva wrote: > Check return value from call to devm_kzalloc() > in order to prevent a NULL pointer dereference. Right, thanks for finding that one! There's another one inside the for loop that is just starting in the context of this patch. Care to fix checking the return value of that devm_kmemdup as well? And someone should perhaps teach Coccinelle about devm_kmemdup... > This issue was detected using Coccinelle and the following semantic patch: > > @@ > expression x; > identifier fld; > @@ > > * x = devm_kzalloc(...); > ... when != x == NULL > x->fld > > One of these blank lines should perhaps be a "Fixes:" tag? Cheers, peda > Signed-off-by: Gustavo A. R. Silva> --- > drivers/iio/multiplexer/iio-mux.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/drivers/iio/multiplexer/iio-mux.c > b/drivers/iio/multiplexer/iio-mux.c > index 37ba007..a8d672b 100644 > --- a/drivers/iio/multiplexer/iio-mux.c > +++ b/drivers/iio/multiplexer/iio-mux.c > @@ -285,6 +285,9 @@ static int mux_configure_channel(struct device *dev, > struct mux *mux, > child->ext_info_cache = devm_kzalloc(dev, >sizeof(*child->ext_info_cache) * >num_ext_info, GFP_KERNEL); > + if (!child->ext_info_cache) > + return -ENOMEM; > + > for (i = 0; i < num_ext_info; ++i) { > child->ext_info_cache[i].size = -1; > >
Re: [PATCH] iio: multiplexer: add NULL check on devm_kzalloc() return value
On 2017-07-07 06:35, Gustavo A. R. Silva wrote: > Hi Peter, > > Quoting Peter Rosin <p...@axentia.se>: > >> On 2017-07-07 00:08, Gustavo A. R. Silva wrote: >>> Check return value from call to devm_kzalloc() >>> in order to prevent a NULL pointer dereference. >> >> Right, thanks for finding that one! There's another one inside the >> for loop that is just starting in the context of this patch. Care >> to fix checking the return value of that devm_kmemdup as well? >> > > Sure, I'll send a new patch shortly. > >> And someone should perhaps teach Coccinelle about devm_kmemdup... >> > > Good catch, I just implemented that script. > >>> This issue was detected using Coccinelle and the following semantic patch: >>> >>> @@ >>> expression x; >>> identifier fld; >>> @@ >>> >>> * x = devm_kzalloc(...); >>> ... when != x == NULL >>> x->fld >>> >>> >> >> One of these blank lines should perhaps be a "Fixes:" tag? >> > > mmm, I don't get this... If you add a Fixes-tag, like below, you help the stable kernel maintainers decide what to look at. In this case it might be overkill since the thing you fix is so fresh and does not apply to any old kernel. But I think it is a good habit... Fixes: 7ba9df54b091 ("iio: multiplexer: new iio category and iio-mux driver") (and it is a bit unusual to see two blank lines before the SoB-tag) Sorry for not spelling it out the first time. Cheers, peda
Re: [PATCH] iio: multiplexer: add NULL check on devm_kzalloc() and devm_kmemdup() return values
On 2017-07-07 06:53, Gustavo A. R. Silva wrote: > Check return values from call to devm_kzalloc() and devm_kmemup() If someone cares enough: s/devm_kmemup/evm_kmemdup/ > in order to prevent a NULL pointer dereference. > > This issue was detected using Coccinelle and the following semantic patch: > > @@ > expression x; > identifier fld; > @@ > > * x = devm_kzalloc(...); >... when != x == NULL >x->fld > > Cc: Peter Rosin <p...@axentia.se> > Signed-off-by: Gustavo A. R. Silva <garsi...@embeddedor.com> Either way, Reviewed-by: Peter Rosin <p...@axentia.se> Thanks!
Re: [PATCH v13 03/10] mux: minimal mux subsystem and gpio-based mux controller
On 2017-04-20 23:53, Peter Rosin wrote: > On 2017-04-18 23:53, Peter Rosin wrote: >> On 2017-04-18 13:44, Greg Kroah-Hartman wrote: >>> On Tue, Apr 18, 2017 at 12:59:50PM +0200, Peter Rosin wrote: >>>> On 2017-04-18 10:51, Greg Kroah-Hartman wrote: >>>>> On Thu, Apr 13, 2017 at 06:43:07PM +0200, Peter Rosin wrote: > > *snip* > >>>>>> +if (mux->idle_state != MUX_IDLE_AS_IS && >>>>>> +mux->idle_state != mux->cached_state) >>>>>> +ret = mux_control_set(mux, mux->idle_state); >>>>>> + >>>>>> +up_read(>lock); >>>>> >>>>> You require a lock to be held for a "global" function? Without >>>>> documentation? Or even a sparse marking? That's asking for trouble... >>>> >>>> Documentation I can handle, but where should I look to understand how I >>>> should add sparse markings? >>> >>> Run sparse on the code and see what it says :) >> >> Will do. > > I just did, and even went through the trouble of getting the bleeding > edge sparse from the git repo when sparse 0.5.0 came up empty, but it's > all silence for me. So, how do I add sparse markings? I looked some more into this, and the markings I find that seem related are __acquire() and __release(). But neither mutex_lock() nor up_read() has markings like that, so adding them when using those kinds of locks in an imbalanced way seems like a sure way of *getting* sparse messages about context imbalance... So, either that, or you are talking about __must_check markings? I feel like I'm missing something, please advise further. Cheers, peda
Re: [PATCH] iio: inkern: fix a static checker error
On 2017-04-25 18:01, Lars-Peter Clausen wrote: > On 04/24/2017 11:32 AM, Peter Rosin wrote: >> On 2017-04-20 23:13, Peter Rosin wrote: >>> On 2017-04-20 23:12, Lars-Peter Clausen wrote: >>>> On 04/20/2017 11:01 PM, Peter Rosin wrote: >>>>> Avoid this smatch error: >>>>> drivers/iio/inkern.c:751 iio_read_avail_channel_raw() error: double >>>>> unlock 'mutex:>indio_dev->info_exist_lock' >>>> >>>> Looks good, but it's not just the smatch error, this is a real issue. This >>>> even seems to be a endless loop, always jumping back to err_unlock. >>> >>> Yes, it should probably go to stable too... >> >> Nope, not an endless loop, but I of course only noticed after sending >> a v2 [1] which falsely stated just that. Ignore that v2 patch and take >> this one instead, for the reasons stated in my followup [2] to that >> message. >> >> Involving stable is probably not needed either... > > Right, my fault for sending you the wrong way. Sorry for that. No no, don't worry, I was already down that path without your help. I just thought everybody would see the obvious bug and the urgency when it was pointed out to them with a patch. Like you did. And I was in a hurry for it to have a slim chance of getting in before 4.12 (but then -rc8 happened instead) so didn't spend a lot of time crafting a thorough commit message (nor analyzing the problem, but why would I when I had a very clear picture of an endless loop on error?). But in the end it wasn't really that obvious, was it? And not that urgent either. Stupid damn bug... Cheers, peda
[PATCH v2] iio: inkern: fix endless loop in error path
If ret ends up negative, mutex_unlock is called repeatedly in an infinite loop, which of course is pretty nasty... Issue found by smatch: drivers/iio/inkern.c:751 iio_read_avail_channel_raw() error: double unlock 'mutex:>indio_dev->info_exist_lock' Fixes: 00c5f80c2fad ("iio: inkern: add helpers to query available values from channels") Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/iio/inkern.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) v1 -> v2 changes: - Be clear about that there is a real nasty issue and that it isn't only about avoiding some insignificant static checker issue. v1: https://lkml.org/lkml/2017/4/20/887 Cheers, peda diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 7a13535dc3e9..a3941bade6a7 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -750,11 +750,9 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, err_unlock: mutex_unlock(>indio_dev->info_exist_lock); - if (ret >= 0 && type != IIO_VAL_INT) { + if (ret >= 0 && type != IIO_VAL_INT) /* raw values are assumed to be IIO_VAL_INT */ ret = -EINVAL; - goto err_unlock; - } return ret; } -- 2.1.4
[PATCH v14 02/11] dt-bindings: document devicetree bindings for mux-controllers and gpio-mux
Allow specifying that a single multiplexer controller can be used to control several parallel multiplexers, thus enabling sharing of the multiplexer controller by different consumers. Add a binding for a first mux controller in the form of a GPIO based mux controller. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/devicetree/bindings/mux/gpio-mux.txt | 69 + .../devicetree/bindings/mux/mux-controller.txt | 157 + MAINTAINERS| 6 + include/dt-bindings/mux/mux.h | 16 +++ 4 files changed, 248 insertions(+) create mode 100644 Documentation/devicetree/bindings/mux/gpio-mux.txt create mode 100644 Documentation/devicetree/bindings/mux/mux-controller.txt create mode 100644 include/dt-bindings/mux/mux.h diff --git a/Documentation/devicetree/bindings/mux/gpio-mux.txt b/Documentation/devicetree/bindings/mux/gpio-mux.txt new file mode 100644 index ..b8f746344d80 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/gpio-mux.txt @@ -0,0 +1,69 @@ +GPIO-based multiplexer controller bindings + +Define what GPIO pins are used to control a multiplexer. Or several +multiplexers, if the same pins control more than one multiplexer. + +Required properties: +- compatible : "gpio-mux" +- mux-gpios : list of gpios used to control the multiplexer, least + significant bit first. +- #mux-control-cells : <0> +* Standard mux-controller bindings as decribed in mux-controller.txt + +Optional properties: +- idle-state : if present, the state the mux will have when idle. The + special state MUX_IDLE_AS_IS is the default. + +The multiplexer state is defined as the number represented by the +multiplexer GPIO pins, where the first pin is the least significant +bit. An active pin is a binary 1, an inactive pin is a binary 0. + +Example: + + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync-1", "in", "out", "sync-2"; + }; + + i2c-mux { + compatible = "i2c-mux"; + i2c-parent = <>; + + mux-controls = <>; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + ssd1307: oled@3c { + /* ... */ + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + /* ... */ + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mux/mux-controller.txt b/Documentation/devicetree/bindings/mux/mux-controller.txt new file mode 100644 index ..4f47e4bd2fa0 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/mux-controller.txt @@ -0,0 +1,157 @@ +Common multiplexer controller bindings +== + +A multiplexer (or mux) controller will have one, or several, consumer devices +that uses the mux controller. Thus, a mux controller can possibly control +several parallel multiplexers. Presumably there will be at least one +multiplexer needed by each consumer, but a single mux controller can of course +control several multiplexers for a single consumer. + +A mux controller provides a number of states to its consumers, and the state +space is a simple zero-based enumeration. I.e. 0-1 for a 2-way multiplexer, +0-7 for an 8-way multiplexer, etc. + + +Consumers +- + +Mux controller consumers should specify a list of mux controllers that they +want to use with a property containing a 'mux-ctrl-list': + + mux-ctrl-list ::= [mux-ctrl-list] + single-mux-ctrl ::= [mux-ctrl-specifier] + mux-ctrl-phandle : phandle to mux controller node + mux-ctrl-specifier : array of #mux-control-cells specifying the +given mux controller (controller specific) + +Mux controller properties should be named "mux-controls". The exact meaning of +each mux controller property must be documented in the device tree bind
[PATCH v14 05/11] iio: inkern: api for manipulating ext_info of iio channels
Extend the inkern api with functions for reading and writing ext_info of iio channels. Acked-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/iio/inkern.c | 60 include/linux/iio/consumer.h | 37 +++ 2 files changed, 97 insertions(+) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 7a13535dc3e9..8292ad4435ea 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -869,3 +869,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val) return ret; } EXPORT_SYMBOL_GPL(iio_write_channel_raw); + +unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) +{ + const struct iio_chan_spec_ext_info *ext_info; + unsigned int i = 0; + + if (!chan->channel->ext_info) + return i; + + for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++) + ++i; + + return i; +} +EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); + +static const struct iio_chan_spec_ext_info *iio_lookup_ext_info( + const struct iio_channel *chan, + const char *attr) +{ + const struct iio_chan_spec_ext_info *ext_info; + + if (!chan->channel->ext_info) + return NULL; + + for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) { + if (!strcmp(attr, ext_info->name)) + return ext_info; + } + + return NULL; +} + +ssize_t iio_read_channel_ext_info(struct iio_channel *chan, + const char *attr, char *buf) +{ + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = iio_lookup_ext_info(chan, attr); + if (!ext_info) + return -EINVAL; + + return ext_info->read(chan->indio_dev, ext_info->private, + chan->channel, buf); +} +EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); + +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, + const char *buf, size_t len) +{ + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = iio_lookup_ext_info(chan, attr); + if (!ext_info) + return -EINVAL; + + return ext_info->write(chan->indio_dev, ext_info->private, + chan->channel, buf, len); +} +EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 47eeec3218b5..5e347a9805fd 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -312,4 +312,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, int *processed, unsigned int scale); +/** + * iio_get_channel_ext_info_count() - get number of ext_info attributes + * connected to the channel. + * @chan: The channel being queried + * + * Returns the number of ext_info attributes + */ +unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan); + +/** + * iio_read_channel_ext_info() - read ext_info attribute from a given channel + * @chan: The channel being queried. + * @attr: The ext_info attribute to read. + * @buf: Where to store the attribute value. Assumed to hold + * at least PAGE_SIZE bytes. + * + * Returns the number of bytes written to buf (perhaps w/o zero termination; + * it need not even be a string), or an error code. + */ +ssize_t iio_read_channel_ext_info(struct iio_channel *chan, + const char *attr, char *buf); + +/** + * iio_write_channel_ext_info() - write ext_info attribute from a given channel + * @chan: The channel being queried. + * @attr: The ext_info attribute to read. + * @buf: The new attribute value. Strings needs to be zero- + * terminated, but the terminator should not be included + * in the below len. + * @len: The size of the new attribute value. + * + * Returns the number of accepted bytes, which should be the same as len. + * An error code can also be returned. + */ +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, + const char *buf, size_t len); + #endif -- 2.1.4
[PATCH v14 03/11] mux: minimal mux subsystem
Add a new minimalistic subsystem that handles multiplexer controllers. When multiplexers are used in various places in the kernel, and the same multiplexer controller can be used for several independent things, there should be one place to implement support for said multiplexer controller. A single multiplexer controller can also be used to control several parallel multiplexers, that are in turn used by different subsystems in the kernel, leading to a need to coordinate multiplexer accesses. The multiplexer subsystem handles this coordination. Thanks go out to Lars-Peter Clausen, Jonathan Cameron, Rob Herring, Wolfram Sang, Paul Gortmaker, Dan Carpenter, Colin Ian King, Greg Kroah-Hartman and last but certainly not least to Philipp Zabel for helpful comments, reviews, patches and general encouragement! Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/ABI/testing/sysfs-class-mux | 16 + Documentation/driver-model/devres.txt | 8 + MAINTAINERS | 3 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/mux/Kconfig | 16 + drivers/mux/Makefile | 5 + drivers/mux/mux-core.c| 593 ++ include/linux/mux/consumer.h | 33 ++ include/linux/mux/driver.h| 111 ++ 10 files changed, 788 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-mux create mode 100644 drivers/mux/Kconfig create mode 100644 drivers/mux/Makefile create mode 100644 drivers/mux/mux-core.c create mode 100644 include/linux/mux/consumer.h create mode 100644 include/linux/mux/driver.h diff --git a/Documentation/ABI/testing/sysfs-class-mux b/Documentation/ABI/testing/sysfs-class-mux new file mode 100644 index ..8715f9c7bd4f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mux @@ -0,0 +1,16 @@ +What: /sys/class/mux/ +Date: April 2017 +KernelVersion: 4.13 +Contact: Peter Rosin <p...@axentia.se> +Description: + The mux/ class sub-directory belongs to the Generic MUX + Framework and provides a sysfs interface for using MUX + controllers. + +What: /sys/class/mux/muxchipN/ +Date: April 2017 +KernelVersion: 4.13 +Contact: Peter Rosin <p...@axentia.se> +Description: + A /sys/class/mux/muxchipN directory is created for each + probed MUX chip where N is a simple enumeration. diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index efb8200819d6..e2343d9cbec7 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -337,6 +337,14 @@ MEM MFD devm_mfd_add_devices() +MUX + devm_mux_chip_alloc() + devm_mux_chip_free() + devm_mux_chip_register() + devm_mux_chip_unregister() + devm_mux_control_get() + devm_mux_control_put() + PER-CPU MEM devm_alloc_percpu() devm_free_percpu() diff --git a/MAINTAINERS b/MAINTAINERS index 7fc06739c8ad..4cfa080878e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8561,8 +8561,11 @@ F: include/linux/spi/mmc_spi.h MULTIPLEXER SUBSYSTEM M: Peter Rosin <p...@axentia.se> S: Maintained +F: Documentation/ABI/testing/mux/sysfs-class-mux* F: Documentation/devicetree/bindings/mux/ F: include/linux/dt-bindings/mux/ +F: include/linux/mux/ +F: drivers/mux/ MULTISOUND SOUND DRIVER M: Andrew Veliath <andre...@usa.net> diff --git a/drivers/Kconfig b/drivers/Kconfig index 117ca14ccf85..a7ea13e1b869 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -204,4 +204,6 @@ source "drivers/fpga/Kconfig" source "drivers/fsi/Kconfig" +source "drivers/mux/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 2eced9afba53..c0436f6dd5a9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -177,3 +177,4 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM)+= nvmem/ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ +obj-$(CONFIG_MULTIPLEXER) += mux/ diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig new file mode 100644 index ..23ab2cde83b1 --- /dev/null +++ b/drivers/mux/Kconfig @@ -0,0 +1,16 @@ +# +# Multiplexer devices +# + +menuconfig MULTIPLEXER + tristate "Multiplexer subsystem" + help + Multiplexer controller subsystem. Multiplexers are used in a + variety of settings, and this subsystem abstracts their use + so that the rest of the kernel sees a common interface. When + multiple parallel multiplexers are controlled by one single + multiplexer controller, this subsystem also coordinates the + multiplexer accesses.
[PATCH v14 04/11] mux: gpio: add mux controller driver for gpio based multiplexers
The driver builds a single multiplexer controller using a number of gpio pins. For N pins, there will be 2^N possible multiplexer states. The GPIO pins can be connected (by the hardware) to several multiplexers, which in that case will be operated in parallel. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig| 18 drivers/mux/Makefile | 1 + drivers/mux/mux-gpio.c | 114 + 3 files changed, 133 insertions(+) create mode 100644 drivers/mux/mux-gpio.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 23ab2cde83b1..e580c2d028d2 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -14,3 +14,21 @@ menuconfig MULTIPLEXER To compile the subsystem as a module, choose M here: the module will be called mux-core. + +if MULTIPLEXER + +config MUX_GPIO + tristate "GPIO-controlled Multiplexer" + depends on GPIOLIB + help + GPIO-controlled Multiplexer controller. + + The driver builds a single multiplexer controller using a number + of gpio pins. For N pins, there will be 2^N possible multiplexer + states. The GPIO pins can be connected (by the hardware) to several + multiplexers, which in that case will be operated in parallel. + + To compile the driver as a module, choose M here: the module will + be called mux-gpio. + +endif diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 09f0299e109d..bb16953f6290 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_MULTIPLEXER) += mux-core.o +obj-$(CONFIG_MUX_GPIO) += mux-gpio.o diff --git a/drivers/mux/mux-gpio.c b/drivers/mux/mux-gpio.c new file mode 100644 index ..468bf1709606 --- /dev/null +++ b/drivers/mux/mux-gpio.c @@ -0,0 +1,114 @@ +/* + * GPIO-controlled multiplexer driver + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct mux_gpio { + struct gpio_descs *gpios; + int *val; +}; + +static int mux_gpio_set(struct mux_control *mux, int state) +{ + struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip); + int i; + + for (i = 0; i < mux_gpio->gpios->ndescs; i++) + mux_gpio->val[i] = (state >> i) & 1; + + gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs, + mux_gpio->gpios->desc, + mux_gpio->val); + + return 0; +} + +static const struct mux_control_ops mux_gpio_ops = { + .set = mux_gpio_set, +}; + +static const struct of_device_id mux_gpio_dt_ids[] = { + { .compatible = "gpio-mux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids); + +static int mux_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = >dev; + struct mux_chip *mux_chip; + struct mux_gpio *mux_gpio; + int pins; + s32 idle_state; + int ret; + + pins = gpiod_count(dev, "mux"); + if (pins < 0) + return pins; + + mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) + + pins * sizeof(*mux_gpio->val)); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + mux_gpio = mux_chip_priv(mux_chip); + mux_gpio->val = (int *)(mux_gpio + 1); + mux_chip->ops = _gpio_ops; + + mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW); + if (IS_ERR(mux_gpio->gpios)) { + ret = PTR_ERR(mux_gpio->gpios); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get gpios\n"); + return ret; + } + WARN_ON(pins != mux_gpio->gpios->ndescs); + mux_chip->mux->states = 1 << pins; + + ret = device_property_read_u32(dev, "idle-state", (u32 *)_state); + if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) { + if (idle_state < 0 || idle_state >= mux_chip->mux->states) { + dev_err(dev, "invalid idle-state %u\n", idle_state); + return -EINVAL; + } + + mux_chip->mux->idle_state = idle_state; + } + + ret = devm_mux_chip_register(dev, mux_chip); + if (ret < 0) + return ret; + + dev_info(dev, "%u-way mux-controller registered\n", +
Re: [PATCH v2] iio: inkern: fix endless loop in error path
Arrh! Actually, this updated description is not at all accurate! It is still pretty nasty to loop back and unlock the mutex again, but the loop isn't endless. The code will only jump back once. That's of course bad enough, but there's also the fact that the bug can only happen if type is not IIO_VAL_INT. And IIUC, that really shouldn't be the case for raw values? Because inkern.c iio_write_channel_raw() certainly implies it. If raw values really are IIO_VAL_INT *always*, then the double unlock should never trigger. And in that case the description of the original v1 patch is much more accurate, since it really mostly is about a static checker issue. So, please ignore this update and pick the original patch instead. I.e. https://lkml.org/lkml/2017/4/20/887 Sorry for the noise. Cheers, peda On 2017-04-24 10:57, Peter Rosin wrote: > If ret ends up negative, mutex_unlock is called repeatedly in an infinite > loop, which of course is pretty nasty... > > Issue found by smatch: > drivers/iio/inkern.c:751 iio_read_avail_channel_raw() error: double unlock > 'mutex:>indio_dev->info_exist_lock' > > Fixes: 00c5f80c2fad ("iio: inkern: add helpers to query available values from > channels") > Signed-off-by: Peter Rosin <p...@axentia.se> > --- > drivers/iio/inkern.c | 4 +--- > 1 file changed, 1 insertion(+), 3 deletions(-) > > v1 -> v2 changes: > - Be clear about that there is a real nasty issue and that it isn't only > about avoiding some insignificant static checker issue. > > v1: https://lkml.org/lkml/2017/4/20/887 > > Cheers, > peda > > diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c > index 7a13535dc3e9..a3941bade6a7 100644 > --- a/drivers/iio/inkern.c > +++ b/drivers/iio/inkern.c > @@ -750,11 +750,9 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, > err_unlock: > mutex_unlock(>indio_dev->info_exist_lock); > > - if (ret >= 0 && type != IIO_VAL_INT) { > + if (ret >= 0 && type != IIO_VAL_INT) > /* raw values are assumed to be IIO_VAL_INT */ > ret = -EINVAL; > - goto err_unlock; > - } > > return ret; > } >
[PATCH v14 07/11] iio: multiplexer: new iio category and iio-mux driver
When a multiplexer changes how an iio device behaves (for example by feeding different signals to an ADC), this driver can be used to create one virtual iio channel for each multiplexer state. Depends on the generic multiplexer subsystem. Cache any ext_info values from the parent iio channel, creating a private copy of the ext_info attributes for each multiplexer state/channel. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- MAINTAINERS | 1 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/multiplexer/Kconfig | 18 ++ drivers/iio/multiplexer/Makefile | 6 + drivers/iio/multiplexer/iio-mux.c | 459 ++ 6 files changed, 486 insertions(+) create mode 100644 drivers/iio/multiplexer/Kconfig create mode 100644 drivers/iio/multiplexer/Makefile create mode 100644 drivers/iio/multiplexer/iio-mux.c diff --git a/MAINTAINERS b/MAINTAINERS index 1cde195bba25..519719069211 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6365,6 +6365,7 @@ M: Peter Rosin <p...@axentia.se> L: linux-...@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt +F: drivers/iio/multiplexer/iio-mux.c IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron <ji...@kernel.org> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index a918270d6f54..b3c8c6ef0dff 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -83,6 +83,7 @@ source "drivers/iio/humidity/Kconfig" source "drivers/iio/imu/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" +source "drivers/iio/multiplexer/Kconfig" source "drivers/iio/orientation/Kconfig" if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 33fa4026f92c..93c769cd99bf 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -28,6 +28,7 @@ obj-y += humidity/ obj-y += imu/ obj-y += light/ obj-y += magnetometer/ +obj-y += multiplexer/ obj-y += orientation/ obj-y += potentiometer/ obj-y += potentiostat/ diff --git a/drivers/iio/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig new file mode 100644 index ..70a044510686 --- /dev/null +++ b/drivers/iio/multiplexer/Kconfig @@ -0,0 +1,18 @@ +# +# Multiplexer drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Multiplexers" + +config IIO_MUX + tristate "IIO multiplexer driver" + select MULTIPLEXER + depends on OF + help + Say yes here to build support for the IIO multiplexer. + + To compile this driver as a module, choose M here: the + module will be called iio-mux. + +endmenu diff --git a/drivers/iio/multiplexer/Makefile b/drivers/iio/multiplexer/Makefile new file mode 100644 index ..68be3c4abd07 --- /dev/null +++ b/drivers/iio/multiplexer/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for industrial I/O multiplexer drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_IIO_MUX) += iio-mux.o diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c new file mode 100644 index ..37ba007f8dca --- /dev/null +++ b/drivers/iio/multiplexer/iio-mux.c @@ -0,0 +1,459 @@ +/* + * IIO multiplexer driver + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mux_ext_info_cache { + char *data; + ssize_t size; +}; + +struct mux_child { + struct mux_ext_info_cache *ext_info_cache; +}; + +struct mux { + int cached_state; + struct mux_control *control; + struct iio_channel *parent; + struct iio_dev *indio_dev; + struct iio_chan_spec *chan; + struct iio_chan_spec_ext_info *ext_info; + struct mux_child *child; +}; + +static int iio_mux_select(struct mux *mux, int idx) +{ + struct mux_child *child = >child[idx]; + struct iio_chan_spec const *chan = >chan[idx]; + int ret; + int i; + + ret = mux_control_select(mux->control, chan->channel); + if (ret < 0) { + mux->cached_state = -1; + return ret; + } + + if (mux->cached_state == chan->channel) + return 0; + + if (chan->ext_info) { + for (i = 0; chan->ext_info[i].name; ++i) { + const char *attr = chan->ext_info[i].name; +
[PATCH v14 09/11] i2c: i2c-mux-gpmux: new driver
This is a general purpose i2c mux that uses a multiplexer controlled by the multiplexer subsystem to do the muxing. The user can select if the mux is to be mux-locked and parent-locked as described in Documentation/i2c/i2c-topology. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Wolfram Sang <w...@the-dreams.de> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/i2c/muxes/Kconfig | 13 +++ drivers/i2c/muxes/Makefile| 1 + drivers/i2c/muxes/i2c-mux-gpmux.c | 173 ++ 3 files changed, 187 insertions(+) create mode 100644 drivers/i2c/muxes/i2c-mux-gpmux.c diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 10b3d17ae3ea..5fb34f24 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -30,6 +30,19 @@ config I2C_MUX_GPIO This driver can also be built as a module. If so, the module will be called i2c-mux-gpio. +config I2C_MUX_GPMUX + tristate "General Purpose I2C multiplexer" + select MULTIPLEXER + depends on OF + help + If you say yes to this option, support will be included for a + general purpose I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which in turn is controlled + by a MUX-controller from the MUX subsystem. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-gpmux. + config I2C_MUX_PCA9541 tristate "NXP PCA9541 I2C Master Selector" help diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 9948fa45037f..af43c6c3e861 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE)+= i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_DEMUX_PINCTRL)+= i2c-demux-pinctrl.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o +obj-$(CONFIG_I2C_MUX_GPMUX)+= i2c-mux-gpmux.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c new file mode 100644 index ..92cf5f48afe6 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -0,0 +1,173 @@ +/* + * General Purpose I2C multiplexer + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +struct mux { + struct mux_control *control; + + bool do_not_deselect; +}; + +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + int ret; + + ret = mux_control_select(mux->control, chan); + mux->do_not_deselect = ret < 0; + + return ret; +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + + if (mux->do_not_deselect) + return 0; + + return mux_control_deselect(mux->control); +} + +static struct i2c_adapter *mux_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + +static const struct of_device_id i2c_mux_of_match[] = { + { .compatible = "i2c-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_of_match); + +static int i2c_mux_probe(struct platform_device *pdev) +{ + struct device *dev = >dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct i2c_mux_core *muxc; + struct mux *mux; + struct i2c_adapter *parent; + int children; + int ret; + + if (!np) + return -ENODEV; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->control = devm_mux_control_get(dev, NULL); + if (IS_ERR(mux->control)) { + if (PTR_ERR(mux->control) != -EPROBE_DEFER) + dev_err(dev, "failed to get control-mux\n"); + return PTR_ERR(mux->control); + } + +
[PATCH v14 10/11] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
Analog Devices ADG792A/G is a triple 4:1 mux. Acked-by: Jonathan Cameron <ji...@kernel.org> Reviewed-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../devicetree/bindings/mux/adi,adg792a.txt| 75 ++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/mux/adi,adg792a.txt diff --git a/Documentation/devicetree/bindings/mux/adi,adg792a.txt b/Documentation/devicetree/bindings/mux/adi,adg792a.txt new file mode 100644 index ..96b787a69f50 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/adi,adg792a.txt @@ -0,0 +1,75 @@ +Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers + +Required properties: +- compatible : "adi,adg792a" or "adi,adg792g" +- #mux-control-cells : <0> if parallel (the three muxes are bound together + with a single mux controller controlling all three muxes), or <1> if + not (one mux controller for each mux). +* Standard mux-controller bindings as described in mux-controller.txt + +Optional properties for ADG792G: +- gpio-controller : if present, #gpio-cells below is required. +- #gpio-cells : should be <2> + - First cell is the GPO line number, i.e. 0 or 1 + - Second cell is used to specify active high (0) + or active low (1) + +Optional properties: +- idle-state : if present, array of states that the mux controllers will have + when idle. The special state MUX_IDLE_AS_IS is the default and + MUX_IDLE_DISCONNECT is also supported. + +States 0 through 3 correspond to signals A through D in the datasheet. + +Example: + + /* +* Three independent mux controllers (of which one is used). +* Mux 0 is disconnected when idle, mux 1 idles in the previously +* selected state and mux 2 idles with signal B. +*/ +{ + mux: mux-controller@50 { + compatible = "adi,adg792a"; + reg = <0x50>; + #mux-control-cells = <1>; + + idle-state = ; + }; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = < 2>; + + channels = "sync-1", "", "out"; + }; + + + /* +* Three parallel muxes with one mux controller, useful e.g. if +* the adc is differential, thus needing two signals to be muxed +* simultaneously for correct operation. +*/ +{ + pmux: mux-controller@50 { + compatible = "adi,adg792a"; + reg = <0x50>; + #mux-control-cells = <0>; + + idle-state = <1>; + }; + }; + + diff-adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync-1", "", "out"; + }; -- 2.1.4
[PATCH v14 11/11] mux: adg792a: add mux controller driver for ADG792A/G
Analog Devices ADG792A/G is a triple 4:1 mux. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig | 12 drivers/mux/Makefile | 1 + drivers/mux/mux-adg792a.c | 157 ++ 3 files changed, 170 insertions(+) create mode 100644 drivers/mux/mux-adg792a.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index e580c2d028d2..34b8284d6d29 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -17,6 +17,18 @@ menuconfig MULTIPLEXER if MULTIPLEXER +config MUX_ADG792A + tristate "Analog Devices ADG792A/ADG792G Multiplexers" + depends on I2C + help + ADG792A and ADG792G Wide Bandwidth Triple 4:1 Multiplexers + + The driver supports both operating the three multiplexers in + parallel and operating them independently. + + To compile the driver as a module, choose M here: the module will + be called mux-adg792a. + config MUX_GPIO tristate "GPIO-controlled Multiplexer" depends on GPIOLIB diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index bb16953f6290..b00a7d37d2fb 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_MULTIPLEXER) += mux-core.o +obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o obj-$(CONFIG_MUX_GPIO) += mux-gpio.o diff --git a/drivers/mux/mux-adg792a.c b/drivers/mux/mux-adg792a.c new file mode 100644 index ..12aa221ab90d --- /dev/null +++ b/drivers/mux/mux-adg792a.c @@ -0,0 +1,157 @@ +/* + * Multiplexer driver for Analog Devices ADG792A/G Triple 4:1 mux + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define ADG792A_LDSW BIT(0) +#define ADG792A_RESETB BIT(1) +#define ADG792A_DISABLE(mux) (0x50 | (mux)) +#define ADG792A_DISABLE_ALL(0x5f) +#define ADG792A_MUX(mux, state)(0xc0 | (((mux) + 1) << 2) | (state)) +#define ADG792A_MUX_ALL(state) (0xc0 | (state)) + +static int adg792a_write_cmd(struct i2c_client *i2c, u8 cmd, int reset) +{ + u8 data = ADG792A_RESETB | ADG792A_LDSW; + + /* ADG792A_RESETB is active low, the chip resets when it is zero. */ + if (reset) + data &= ~ADG792A_RESETB; + + return i2c_smbus_write_byte_data(i2c, cmd, data); +} + +static int adg792a_set(struct mux_control *mux, int state) +{ + struct i2c_client *i2c = to_i2c_client(mux->chip->dev.parent); + u8 cmd; + + if (mux->chip->controllers == 1) { + /* parallel mux controller operation */ + if (state == MUX_IDLE_DISCONNECT) + cmd = ADG792A_DISABLE_ALL; + else + cmd = ADG792A_MUX_ALL(state); + } else { + unsigned int controller = mux_control_get_index(mux); + + if (state == MUX_IDLE_DISCONNECT) + cmd = ADG792A_DISABLE(controller); + else + cmd = ADG792A_MUX(controller, state); + } + + return adg792a_write_cmd(i2c, cmd, 0); +} + +static const struct mux_control_ops adg792a_ops = { + .set = adg792a_set, +}; + +static int adg792a_probe(struct i2c_client *i2c, +const struct i2c_device_id *id) +{ + struct device *dev = >dev; + struct mux_chip *mux_chip; + s32 idle_state[3]; + u32 cells; + int ret; + int i; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + ret = device_property_read_u32(dev, "#mux-control-cells", ); + if (ret < 0) + return ret; + if (cells >= 2) + return -EINVAL; + + mux_chip = devm_mux_chip_alloc(dev, cells ? 3 : 1, 0); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + mux_chip->ops = _ops; + + ret = adg792a_write_cmd(i2c, ADG792A_DISABLE_ALL, 1); + if (ret < 0) + return ret; + + ret = device_property_read_u32_array(dev, "idle-state", +(u32 *)idle_state, +mux_chip->controllers); + if (ret < 0) { + idle_state[0] = MUX_IDLE_AS_IS; + idle_state[1] = MUX_IDLE_AS_IS; + idle_state[2] = MUX_IDLE_AS_IS; + } + + for (i = 0; i < mux_chip->controllers; ++i) { + struct mux_control *mux = _chip->mux[i]; + + mux->states = 4; + +
[PATCH v14 08/11] dt-bindings: i2c: i2c-mux: document general purpose i2c-mux bindings
Describe how a general purpose multiplexer controller is used to mux an i2c bus. Acked-by: Jonathan Cameron <ji...@kernel.org> Reviewed-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../devicetree/bindings/i2c/i2c-mux-gpmux.txt | 99 ++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt new file mode 100644 index ..2907dab56298 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt @@ -0,0 +1,99 @@ +General Purpose I2C Bus Mux + +This binding describes an I2C bus multiplexer that uses a mux controller +from the mux subsystem to route the I2C signals. + + .-. .-. + | dev | | dev | +..'-' '-' +| SoC| || +|| .+' +| .--. | .--+child bus A, on MUX value set to 0 +| | I2C |-|--| Mux | +| '--' | '--+---+child bus B, on MUX value set to 1 +| .--. | |'--++. +| | MUX- | | | ||| +| | Ctrl |-|-+.-. .-. .-. +| '--' | | dev | | dev | | dev | +'' '-' '-' '-' + +Required properties: +- compatible: i2c-mux +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side + port is connected to. +- mux-controls: The phandle of the mux controller to use for operating the + mux. +* Standard I2C mux properties. See i2c-mux.txt in this directory. +* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number + is also the mux-controller state described in ../mux/mux-controller.txt + +Optional properties: +- mux-locked: If present, explicitly allow unrelated I2C transactions on the + parent I2C adapter at these times: + + during setup of the multiplexer + + between setup of the multiplexer and the child bus I2C transaction + + between the child bus I2C transaction and releasing of the multiplexer + + during releasing of the multiplexer + However, I2C transactions to devices behind all I2C multiplexers connected + to the same parent adapter that this multiplexer is connected to are blocked + for the full duration of the complete multiplexed I2C transaction (i.e. + including the times covered by the above list). + If mux-locked is not present, the multiplexer is assumed to be parent-locked. + This means that no unrelated I2C transactions are allowed on the parent I2C + adapter for the complete multiplexed I2C transaction. + The properties of mux-locked and parent-locked multiplexers are discussed + in more detail in Documentation/i2c/i2c-topology. + +For each i2c child node, an I2C child bus will be created. They will +be numbered based on their order in the device tree. + +Whenever an access is made to a device on a child bus, the value set +in the relevant node's reg property will be set as the state in the +mux controller. + +Example: + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + i2c-mux { + compatible = "i2c-mux"; + mux-locked; + i2c-parent = <>; + + mux-controls = <>; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + ssd1307: oled@3c { + compatible = "solomon,ssd1307fb-i2c"; + reg = <0x3c>; + pwms = < 4 3000>; + reset-gpios = < 7 1>; + reset-active-low; + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + compatible = "nxp,pca9555"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x20>; + }; + }; + }; -- 2.1.4
[PATCH v14 06/11] dt-bindings: iio: io-channel-mux: document io-channel-mux bindings
Describe how a multiplexer can be used to select which signal is fed to an io-channel. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../bindings/iio/multiplexer/io-channel-mux.txt| 39 ++ MAINTAINERS| 6 2 files changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt new file mode 100644 index ..c82794002595 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt @@ -0,0 +1,39 @@ +I/O channel multiplexer bindings + +If a multiplexer is used to select which hardware signal is fed to +e.g. an ADC channel, these bindings describe that situation. + +Required properties: +- compatible : "io-channel-mux" +- io-channels : Channel node of the parent channel that has multiplexed + input. +- io-channel-names : Should be "parent". +- #address-cells = <1>; +- #size-cells = <0>; +- mux-controls : Mux controller node to use for operating the mux +- channels : List of strings, labeling the mux controller states. + +For each non-empty string in the channels property, an io-channel will +be created. The number of this io-channel is the same as the index into +the list of strings in the channels property, and also matches the mux +controller state. The mux controller state is described in +../mux/mux-controller.txt + +Example: + mux: mux-controller { + compatible = "mux-gpio"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync", "in", "system-regulator"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 4cfa080878e2..1cde195bba25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6360,6 +6360,12 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt F: drivers/iio/adc/envelope-detector.c +IIO MULTIPLEXER +M: Peter Rosin <p...@axentia.se> +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt + IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron <ji...@kernel.org> R: Hartmut Knaack <knaac...@gmx.de> -- 2.1.4
[PATCH v14 01/11] devres: trivial whitespace fix
Everything else is indented with two spaces, so fix the odd one out. Acked-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/driver-model/devres.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index bf34d5b3a733..efb8200819d6 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -335,7 +335,7 @@ MEM devm_kzalloc() MFD - devm_mfd_add_devices() + devm_mfd_add_devices() PER-CPU MEM devm_alloc_percpu() -- 2.1.4
[PATCH v14 00/11] mux controller abstraction and iio/i2c muxes
ux state, it was possible to drop the state member from the mux_child struct. - cleanup dt bindings for i2c-mux-simple, it had some of copy-paste problems from ots origin (i2c-mux-gpio). - select the mux control subsystem in config for the i2c-mux-simple driver. - add entries to MAINTAINERS and my sign-off, I'm now satisfied and know nothing in this to be ashamed of. v1 -> v2 changes: https://lkml.org/lkml/2016/11/17/918 - fixup export of mux_control_put reported by kbuild - drop devicetree iio-ext-info property as noted by Lars-Peter, and replace the functionality by exposing all ext_info attributes of the parent channel for each of the muxed channels. A cache on top of that and each muxed channel gets its own view of the ext_info of the parent channel. - implement idle-state for muxes - clear out the cache on failure in order to force a mux update on the following use - cleanup the probe of i2c-mux-simple driver - fix a bug in the i2c-mux-simple driver, where failure in the selection of the mux caused a deadlock when the mux was later unconditionally deselected. v1: https://lkml.org/lkml/2016/11/16/812 Cheers, peda Peter Rosin (11): devres: trivial whitespace fix dt-bindings: document devicetree bindings for mux-controllers and gpio-mux mux: minimal mux subsystem mux: gpio: add mux controller driver for gpio based multiplexers iio: inkern: api for manipulating ext_info of iio channels dt-bindings: iio: io-channel-mux: document io-channel-mux bindings iio: multiplexer: new iio category and iio-mux driver dt-bindings: i2c: i2c-mux: document general purpose i2c-mux bindings i2c: i2c-mux-gpmux: new driver dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux mux: adg792a: add mux controller driver for ADG792A/G Documentation/ABI/testing/sysfs-class-mux | 16 + .../devicetree/bindings/i2c/i2c-mux-gpmux.txt | 99 .../bindings/iio/multiplexer/io-channel-mux.txt| 39 ++ .../devicetree/bindings/mux/adi,adg792a.txt| 75 +++ Documentation/devicetree/bindings/mux/gpio-mux.txt | 69 +++ .../devicetree/bindings/mux/mux-controller.txt | 157 ++ Documentation/driver-model/devres.txt | 10 +- MAINTAINERS| 16 + drivers/Kconfig| 2 + drivers/Makefile | 1 + drivers/i2c/muxes/Kconfig | 13 + drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/i2c-mux-gpmux.c | 173 ++ drivers/iio/Kconfig| 1 + drivers/iio/Makefile | 1 + drivers/iio/inkern.c | 60 +++ drivers/iio/multiplexer/Kconfig| 18 + drivers/iio/multiplexer/Makefile | 6 + drivers/iio/multiplexer/iio-mux.c | 459 drivers/mux/Kconfig| 46 ++ drivers/mux/Makefile | 7 + drivers/mux/mux-adg792a.c | 157 ++ drivers/mux/mux-core.c | 593 + drivers/mux/mux-gpio.c | 114 include/dt-bindings/mux/mux.h | 16 + include/linux/iio/consumer.h | 37 ++ include/linux/mux/consumer.h | 33 ++ include/linux/mux/driver.h | 111 28 files changed, 2329 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-class-mux create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt create mode 100644 Documentation/devicetree/bindings/mux/adi,adg792a.txt create mode 100644 Documentation/devicetree/bindings/mux/gpio-mux.txt create mode 100644 Documentation/devicetree/bindings/mux/mux-controller.txt create mode 100644 drivers/i2c/muxes/i2c-mux-gpmux.c create mode 100644 drivers/iio/multiplexer/Kconfig create mode 100644 drivers/iio/multiplexer/Makefile create mode 100644 drivers/iio/multiplexer/iio-mux.c create mode 100644 drivers/mux/Kconfig create mode 100644 drivers/mux/Makefile create mode 100644 drivers/mux/mux-adg792a.c create mode 100644 drivers/mux/mux-core.c create mode 100644 drivers/mux/mux-gpio.c create mode 100644 include/dt-bindings/mux/mux.h create mode 100644 include/linux/mux/consumer.h create mode 100644 include/linux/mux/driver.h -- 2.1.4
Re: [PATCH] iio: inkern: fix a static checker error
On 2017-04-20 23:13, Peter Rosin wrote: > On 2017-04-20 23:12, Lars-Peter Clausen wrote: >> On 04/20/2017 11:01 PM, Peter Rosin wrote: >>> Avoid this smatch error: >>> drivers/iio/inkern.c:751 iio_read_avail_channel_raw() error: double unlock >>> 'mutex:>indio_dev->info_exist_lock' >> >> Looks good, but it's not just the smatch error, this is a real issue. This >> even seems to be a endless loop, always jumping back to err_unlock. > > Yes, it should probably go to stable too... Nope, not an endless loop, but I of course only noticed after sending a v2 [1] which falsely stated just that. Ignore that v2 patch and take this one instead, for the reasons stated in my followup [2] to that message. Involving stable is probably not needed either... Cheers, peda [1] https://lkml.org/lkml/2017/4/24/179 [2] https://lkml.org/lkml/2017/4/24/221 >>> >>> Fixes: 00c5f80c2fad ("iio: inkern: add helpers to query available values >>> from channels") >>> Signed-off-by: Peter Rosin <p...@axentia.se> >> >>> --- >>> drivers/iio/inkern.c | 4 +--- >>> 1 file changed, 1 insertion(+), 3 deletions(-) >>> >>> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c >>> index 7a13535dc3e9..a3941bade6a7 100644 >>> --- a/drivers/iio/inkern.c >>> +++ b/drivers/iio/inkern.c >>> @@ -750,11 +750,9 @@ int iio_read_avail_channel_raw(struct iio_channel >>> *chan, >>> err_unlock: >>> mutex_unlock(>indio_dev->info_exist_lock); >>> >>> - if (ret >= 0 && type != IIO_VAL_INT) { >>> + if (ret >= 0 && type != IIO_VAL_INT) >>> /* raw values are assumed to be IIO_VAL_INT */ >>> ret = -EINVAL; >>> - goto err_unlock; >>> - } >>> >>> return ret; >>> } >>> >> >
Re: [RFC v2 2/2] mux: mmio-based syscon mux controller
On 2017-04-24 18:12, Philipp Zabel wrote: > This adds a driver for mmio-based syscon multiplexers controlled by a > single bitfield in a syscon register range. Single bitfield? > > Signed-off-by: Philipp Zabel> --- > Changes since v1: > - Renamed MUX_SYSCON to MUX_MMIO. Instead of obtaining the regmap via syscon, >this could just as well map its own MMIO register range if a reg property >was added. > - Replaced reg, bit-mask, and bit-shift properties with mux-reg-masks array >to allow defining multiple mux bit-fields per mmio-mux instance. > - Changed mux-control-cells value to <1>, the cell value is an index into >the mux-reg-masks array. > - Replaced idle-state with idle-states array. > - Stopped clobbering mux->cached_state, that is internal to the mux core. > --- > drivers/mux/Kconfig| 13 > drivers/mux/Makefile | 1 + > drivers/mux/mux-mmio.c | 162 > + > 3 files changed, 176 insertions(+) > create mode 100644 drivers/mux/mux-mmio.c > > diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig > index 34b8284d6d29e..e2bee25a1586e 100644 > --- a/drivers/mux/Kconfig > +++ b/drivers/mux/Kconfig > @@ -43,4 +43,17 @@ config MUX_GPIO > To compile the driver as a module, choose M here: the module will > be called mux-gpio. > > +config MUX_MMIO > + tristate "MMIO register bitfield-controlled Multiplexer" > + depends on OF && MFD_SYSCON > + help > + MMIO register bitfield-controlled Multiplexer controller. > + > + The driver builds a single multiplexer controller using a bitfield > + in a syscon register. For N bit wide bitfields, there will be 2^N > + possible multiplexer states. Multiple multiplexer controllers > + > + To compile the driver as a module, choose M here: the module will > + be called mux-mmio. > + > endif > diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile > index b00a7d37d2fbe..6bac5b0fea137 100644 > --- a/drivers/mux/Makefile > +++ b/drivers/mux/Makefile > @@ -5,3 +5,4 @@ > obj-$(CONFIG_MULTIPLEXER)+= mux-core.o > obj-$(CONFIG_MUX_ADG792A)+= mux-adg792a.o > obj-$(CONFIG_MUX_GPIO) += mux-gpio.o > +obj-$(CONFIG_MUX_MMIO) += mux-mmio.o > diff --git a/drivers/mux/mux-mmio.c b/drivers/mux/mux-mmio.c > new file mode 100644 > index 0..0b011fb59f99b > --- /dev/null > +++ b/drivers/mux/mux-mmio.c > @@ -0,0 +1,162 @@ > +/* > + * MMIO register bitfield-controlled multiplexer driver > + * > + * Copyright (C) 2017 Pengutronix, Philipp Zabel > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static int mux_mmio_set(struct mux_control *mux, int state) > +{ > + struct regmap_field **fields = mux_chip_priv(mux->chip); > + int index = mux - mux->chip->mux; mux_control_get_index(mux) does exactly this. > + > + return regmap_field_write(fields[index], state); > +} > + > +static const struct mux_control_ops mux_mmio_ops = { > + .set = mux_mmio_set, > +}; > + > +static const struct of_device_id mux_mmio_dt_ids[] = { > + { .compatible = "mmio-mux", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, mux_mmio_dt_ids); > + > +static int mux_mmio_probe(struct platform_device *pdev) > +{ > + struct device *dev = >dev; > + struct device_node *np = dev->of_node; > + struct regmap_field **fields; > + struct mux_chip *mux_chip; > + struct regmap *regmap; > + s32 *idle_states; > + u32 *reg_masks; > + int num_fields; > + int ret; > + int i; > + > + regmap = syscon_node_to_regmap(np->parent); > + if (IS_ERR(regmap)) { > + ret = PTR_ERR(regmap); > + dev_err(dev, "failed to get syscon regmap: %d\n", ret); > + return ret; > + } > + > + ret = of_property_count_u32_elems(np, "mux-reg-masks"); > + if (ret == 0 || ret % 2) > + ret = -EINVAL; > + if (ret > 0) { > + num_fields = ret / 2; > + > + reg_masks = devm_kmalloc(dev, ret * sizeof(u32), GFP_KERNEL); > + if (!reg_masks) > + return -ENOMEM; > + > + ret = of_property_read_u32_array(np, "mux-reg-masks", > + reg_masks, ret); > + } > + if (ret < 0) { > + dev_err(dev, "mux-reg-masks property missing or invalid: %d\n", > + ret); > + return ret; > + } > + > + mux_chip = devm_mux_chip_alloc(dev, num_fields, num_fields * > +sizeof(*fields)); > + if (IS_ERR(mux_chip)) > + return
Re: [PATCH v14 00/11] mux controller abstraction and iio/i2c muxes
On 2017-04-24 12:52, Philipp Zabel wrote: > On Mon, 2017-04-24 at 10:36 +0200, Peter Rosin wrote: >> Hi! >> >> The big change since v13 is that the mux state is now locked with a mutex >> instead of an rwsem. Other that that, it is mostly restructuring and doc >> changes. There are a few other "real" changes as well, but those changes >> feel kind of minor. I guess what I'm trying to say is that although the >> list of changes for v14 is longish, it's still basically the same as last >> time. > > I have hooked this up to the video-multiplexer and now I trigger > the lockdep debug_check_no_locks_held error message when selecting the > mux input from userspace: > > $ media-ctl --links "'imx6-mipi-csi2':1->'ipu1_csi0_mux':0[1]" > [ 66.258368] > [ 66.259919] = > [ 66.265369] [ BUG: media-ctl/258 still has locks held! ] > [ 66.270810] 4.11.0-rc8-20170424-1+ #1305 Not tainted > [ 66.275863] - > [ 66.282158] 1 lock held by media-ctl/258: > [ 66.286464] #0: (>lock){+.+.+.}, at: [<8074a6c0>] > mux_control_select+0x24/0x50 > [ 66.294424] > [ 66.294424] stack backtrace: > [ 66.298980] CPU: 0 PID: 258 Comm: media-ctl Not tainted 4.11.0-rc8+ #1305 > [ 66.306771] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) > [ 66.313334] Backtrace: > [ 66.315858] [<8010e5a4>] (dump_backtrace) from [<8010e8ac>] > (show_stack+0x20/0x24) > [ 66.323473] r7:80e518d0 r6:80e518d0 r5:600d0013 r4: > [ 66.329190] [<8010e88c>] (show_stack) from [<80496cf4>] > (dump_stack+0xb0/0xdc) > [ 66.336470] [<80496c44>] (dump_stack) from [<8017e404>] > (debug_check_no_locks_held+0xb8/0xbc) > [ 66.345043] r9:ae8566b8 r8:ad88dc84 r7:ad88df58 r6:ad88dc84 r5:ad88df58 > r4:ae856400 > [ 66.352837] [<8017e34c>] (debug_check_no_locks_held) from [<8012b258>] > (do_exit+0x79c/0xcc8) > [ 66.361321] [<8012aabc>] (do_exit) from [<8012d25c>] > (do_group_exit+0x4c/0xcc) > [ 66.368581] r7:00f8 > [ 66.371161] [<8012d210>] (do_group_exit) from [<8012d2fc>] > (__wake_up_parent+0x0/0x30) > [ 66.379120] r7:00f8 r6:76f71798 r5: r4:0001 > [ 66.384837] [<8012d2dc>] (SyS_exit_group) from [<80109380>] > (ret_fast_syscall+0x0/0x1c) > > That correctly warns that the media-ctl process caused the mux->lock to > be locked and still held when the process exited. Do we need a usage > counter based mechanism for muxes that are (indirectly) controlled from > userspace? Ok, so the difference is probably that the rwsem locking primitive don't have any lockdep checking hooked up. Because the rwsem was definitely held in the same way in v13 as the mutex is now held in v14, so there's no fundamental difference. So, yes, we can use some kind of refcount scheme to not hold an actual mutex for the duration of the mux select/deselect, but that doesn't really change anything. Userspace is still locking something, and that seems dangerous. How do you make sure that mux_control_deselect is called as it should? What I don't like about abandoning the lock is that there is still a need to support the multi-consumer case and then you need some way of keeping track of waiters. A lock does this, and any attempt to open code that will get messy. What might be better is to support some kind of exclusive mux, i.e. a mux that only allows one consumer per mux controller. Then the mux core could trust that exclusive consumer to not fuck things up for itself and thus have no lock at all for select/deselect for the exclusive case. Or perhaps keep a refcount (as you suggested) for the exclusive case so that mux_control_try_select still makes sense, because you still want that, right? The question then becomes how to best tell the mux core that you want an exclusive mux. I see two options. Either you declare a mux controller as exclusive up front somehow (in the device tree presumably), or you add a mux_control_get_exclusive call that makes further calls to mux_control_get{,_exclusive} fail with -EBUSY. I think I like the latter better, if that can be made to work... Cheers, peda
Re: [RFC v2 1/2] dt-bindings: add mmio-based syscon mux controller DT bindings
On 2017-04-24 18:12, Philipp Zabel wrote: > This adds device tree binding documentation for mmio-based syscon > multiplexers controlled by a single bitfield in a syscon register > range. Single bitfield? > > Signed-off-by: Philipp Zabel> --- > Changes since v1: > - Replaced reg, bit-mask, and bit-shift properties with mux-reg-masks array >to allow defining multiple mux bit-fields per mmio-mux instance. > - Changed mux-control-cells value to <1>, the cell value is an index into >the mux-reg-masks array. > - Replaced idle-state with idle-states array. > --- > Documentation/devicetree/bindings/mux/mmio-mux.txt | 60 > ++ > 1 file changed, 60 insertions(+) > create mode 100644 Documentation/devicetree/bindings/mux/mmio-mux.txt > > diff --git a/Documentation/devicetree/bindings/mux/mmio-mux.txt > b/Documentation/devicetree/bindings/mux/mmio-mux.txt > new file mode 100644 > index 0..99282fa761c55 > --- /dev/null > +++ b/Documentation/devicetree/bindings/mux/mmio-mux.txt > @@ -0,0 +1,60 @@ > +MMIO register bitfield-based multiplexer controller bindings > + > +Define register bitfields to be used to control multiplexers. The parent > +device tree node must be a syscon node to provide register access. > + > +Required properties: > +- compatible : "mmio-mux" > +- #mux-control-cells : <1> > +- mux-reg-masks : an array of register offset and pre-shifted bitfield mask > + pairs, each describing a single mux control. > +* Standard mux-controller bindings as decribed in mux-controller.txt > + > +Optional properties: > +- idle-states : if present, the state the muxes will have when idle. The > + special state MUX_IDLE_AS_IS is the default. > + > +The multiplexer state is defined as the value of the bitfield described > +by the reg, bit-mask, and bit-shift properties, accessed through the parent > +syscon. This paragraph needs updating. > + > +Example: > + > + syscon { > + compatible = "syscon"; > + > + mux: mux-controller@3 { You shouldn't do ...@3 if you don't have a reg property. Cheers, peda > + compatible = "mmio-mux"; > + #mux-control-cells = <1>; > + > + mux-reg-masks = <0x3 0x30>, /* 0: reg 0x3, bits 5:4 */ > + <0x3 0x40>, /* 1: reg 0x3, bit 6 */ > + idle-states = , <0>; > + }; > + }; > + > + video-mux { > + compatible = "video-mux"; > + mux-controls = < 0>; > + > + ports { > + /* inputs 0..3 */ > + port@0 { > + reg = <0>; > + }; > + port@1 { > + reg = <1>; > + }; > + port@2 { > + reg = <2>; > + }; > + port@3 { > + reg = <3>; > + }; > + > + /* output */ > + port@4 { > + reg = <4>; > + }; > + }; > + }; >
Re: [PATCH v14 00/11] mux controller abstraction and iio/i2c muxes
On 2017-04-24 16:10, Philipp Zabel wrote: > On Mon, 2017-04-24 at 13:37 +0200, Peter Rosin wrote: >> On 2017-04-24 12:52, Philipp Zabel wrote: >>> On Mon, 2017-04-24 at 10:36 +0200, Peter Rosin wrote: >>>> Hi! >>>> >>>> The big change since v13 is that the mux state is now locked with a mutex >>>> instead of an rwsem. Other that that, it is mostly restructuring and doc >>>> changes. There are a few other "real" changes as well, but those changes >>>> feel kind of minor. I guess what I'm trying to say is that although the >>>> list of changes for v14 is longish, it's still basically the same as last >>>> time. >>> >>> I have hooked this up to the video-multiplexer and now I trigger >>> the lockdep debug_check_no_locks_held error message when selecting the >>> mux input from userspace: >>> >>> $ media-ctl --links "'imx6-mipi-csi2':1->'ipu1_csi0_mux':0[1]" >>> [ 66.258368] >>> [ 66.259919] = >>> [ 66.265369] [ BUG: media-ctl/258 still has locks held! ] >>> [ 66.270810] 4.11.0-rc8-20170424-1+ #1305 Not tainted >>> [ 66.275863] - >>> [ 66.282158] 1 lock held by media-ctl/258: >>> [ 66.286464] #0: (>lock){+.+.+.}, at: [<8074a6c0>] >>> mux_control_select+0x24/0x50 >>> [ 66.294424] >>> [ 66.294424] stack backtrace: >>> [ 66.298980] CPU: 0 PID: 258 Comm: media-ctl Not tainted 4.11.0-rc8+ #1305 >>> [ 66.306771] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) >>> [ 66.313334] Backtrace: >>> [ 66.315858] [<8010e5a4>] (dump_backtrace) from [<8010e8ac>] >>> (show_stack+0x20/0x24) >>> [ 66.323473] r7:80e518d0 r6:80e518d0 r5:600d0013 r4: >>> [ 66.329190] [<8010e88c>] (show_stack) from [<80496cf4>] >>> (dump_stack+0xb0/0xdc) >>> [ 66.336470] [<80496c44>] (dump_stack) from [<8017e404>] >>> (debug_check_no_locks_held+0xb8/0xbc) >>> [ 66.345043] r9:ae8566b8 r8:ad88dc84 r7:ad88df58 r6:ad88dc84 r5:ad88df58 >>> r4:ae856400 >>> [ 66.352837] [<8017e34c>] (debug_check_no_locks_held) from [<8012b258>] >>> (do_exit+0x79c/0xcc8) >>> [ 66.361321] [<8012aabc>] (do_exit) from [<8012d25c>] >>> (do_group_exit+0x4c/0xcc) >>> [ 66.368581] r7:00f8 >>> [ 66.371161] [<8012d210>] (do_group_exit) from [<8012d2fc>] >>> (__wake_up_parent+0x0/0x30) >>> [ 66.379120] r7:00f8 r6:76f71798 r5: r4:0001 >>> [ 66.384837] [<8012d2dc>] (SyS_exit_group) from [<80109380>] >>> (ret_fast_syscall+0x0/0x1c) >>> >>> That correctly warns that the media-ctl process caused the mux->lock to >>> be locked and still held when the process exited. Do we need a usage >>> counter based mechanism for muxes that are (indirectly) controlled from >>> userspace? > [...] >> The question then becomes how to best tell the mux core that you want >> an exclusive mux. I see two options. Either you declare a mux controller >> as exclusive up front somehow (in the device tree presumably), or you >> add a mux_control_get_exclusive call that makes further calls to >> mux_control_get{,_exclusive} fail with -EBUSY. I think I like the >> latter better, if that can be made to work... > > How about an atomic use_count on the mux_control, a bool shared that is > only set by the first consumer, and controls whether selecting locks? That has the drawback that it is hard to restore the mux-control in a safe way so that exclusive consumers are allowed after the last shared consumer puts the mux away. Agreed, it's a corner case, but I had this very similar patch going through the compiler when I got this mail. Does it work as well as what you suggested? Cheers, peda diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index e2343d9cbec7..5a7352a83124 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -342,7 +342,8 @@ MUX devm_mux_chip_free() devm_mux_chip_register() devm_mux_chip_unregister() - devm_mux_control_get() + devm_mux_control_get_shared() + devm_mux_control_get_exclusive() devm_mux_control_put() PER-CPU MEM diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c index 92cf5f48afe6..135d6baaebe5 100644 --- a/drivers/i2c/muxes/i2c-mux-gpmux.c +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -87,7 +87,7
Re: [PATCH v14 00/11] mux controller abstraction and iio/i2c muxes
On 2017-04-24 16:59, Philipp Zabel wrote: > On Mon, 2017-04-24 at 16:36 +0200, Peter Rosin wrote: > [...] >>> How about an atomic use_count on the mux_control, a bool shared that is >>> only set by the first consumer, and controls whether selecting locks? >> >> That has the drawback that it is hard to restore the mux-control in a safe >> way so that exclusive consumers are allowed after the last shared consumer >> puts the mux away. > > True. > >> Agreed, it's a corner case, but I had this very similar >> patch going through the compiler when I got this mail. Does it work as well >> as what you suggested? > > Yes, this patch works just as well. Right, as expected :-) However, I don't like it much. It divides the mux consumers into two camps in a way that makes it difficult to select which camp a consumer should be in. E.g. consider the iio-mux. The current implementation only supports quick accesses that fit the mux_control_get_shared case. But if that mux in the future needs to grow continuous buffered accesses, I think there will be pressure to switch it over to the exclusive mode. Because that is a lot closer to what you are doing with the video-mux. And then what? It will be impossible to predict if the end user is going to use buffered accesses or not... So, I think the best approach is to skip the distinction between shared and exclusive consumers and instead implement the locking with an ordinary semaphore (instead of the old rwsem or the current mutex). Semaphores don't have the property that the same task should down/up them (mutexes require that for lock/unlock, and is also the reason for the lockdep complaint) and thus fits better for long-time use such as yours or the above iio-mux with buffered accesses. It should also hopefully be cheaper that an rwsem, and not have any downgrade_write calls thus possibly keeping Greg sufficiently happy... Sure, consumers can still dig themselves into a hole by not calling deselect as they should, but at least I think it can be made to work w/o dividing the consumers... Cheers, peda
Re: [PATCH v14 00/11] mux controller abstraction and iio/i2c muxes
On 2017-04-25 16:16, Peter Rosin wrote: > On 2017-04-24 16:59, Philipp Zabel wrote: >> On Mon, 2017-04-24 at 16:36 +0200, Peter Rosin wrote: >> [...] >>>> How about an atomic use_count on the mux_control, a bool shared that is >>>> only set by the first consumer, and controls whether selecting locks? >>> >>> That has the drawback that it is hard to restore the mux-control in a safe >>> way so that exclusive consumers are allowed after the last shared consumer >>> puts the mux away. >> >> True. >> >>> Agreed, it's a corner case, but I had this very similar >>> patch going through the compiler when I got this mail. Does it work as well >>> as what you suggested? >> >> Yes, this patch works just as well. > > Right, as expected :-) However, I don't like it much. It divides the mux > consumers into two camps in a way that makes it difficult to select which > camp a consumer should be in. > > E.g. consider the iio-mux. The current implementation only supports quick > accesses that fit the mux_control_get_shared case. But if that mux in the > future needs to grow continuous buffered accesses, I think there will be > pressure to switch it over to the exclusive mode. Because that is a lot > closer to what you are doing with the video-mux. And then what? It will be > impossible to predict if the end user is going to use buffered accesses or > not... > > So, I think the best approach is to skip the distinction between shared > and exclusive consumers and instead implement the locking with an ordinary > semaphore (instead of the old rwsem or the current mutex). Semaphores don't > have the property that the same task should down/up them (mutexes require > that for lock/unlock, and is also the reason for the lockdep complaint) and > thus fits better for long-time use such as yours or the above iio-mux with > buffered accesses. It should also hopefully be cheaper that an rwsem, and > not have any downgrade_write calls thus possibly keeping Greg sufficiently > happy... > > Sure, consumers can still dig themselves into a hole by not calling deselect > as they should, but at least I think it can be made to work w/o dividing the > consumers... Like this (only compile-tested). Philipp, it should work the same as with the rwsem in v13 and earlier. At least for your case... Cheers, peda diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c index c02fa4dd2d09..f99b70d4e319 100644 --- a/drivers/mux/mux-core.c +++ b/drivers/mux/mux-core.c @@ -116,7 +116,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev, struct mux_control *mux = _chip->mux[i]; mux->chip = mux_chip; - mutex_init(>lock); + sema_init(>lock, 1); mux->cached_state = MUX_CACHE_UNKNOWN; mux->idle_state = MUX_IDLE_AS_IS; } @@ -372,12 +372,14 @@ int mux_control_select(struct mux_control *mux, unsigned int state) { int ret; - mutex_lock(>lock); + ret = down_killable(>lock); + if (ret < 0) + return ret; ret = __mux_control_select(mux, state); if (ret < 0) - mutex_unlock(>lock); + up(>lock); return ret; } @@ -399,13 +401,13 @@ int mux_control_try_select(struct mux_control *mux, unsigned int state) { int ret; - if (!mutex_trylock(>lock)) + if (down_trylock(>lock)) return -EBUSY; ret = __mux_control_select(mux, state); if (ret < 0) - mutex_unlock(>lock); + up(>lock); return ret; } @@ -427,7 +429,7 @@ int mux_control_deselect(struct mux_control *mux) mux->idle_state != mux->cached_state) ret = mux_control_set(mux, mux->idle_state); - mutex_unlock(>lock); + up(>lock); return ret; } diff --git a/include/linux/mux/driver.h b/include/linux/mux/driver.h index 95269f40670a..43f65f80c275 100644 --- a/include/linux/mux/driver.h +++ b/include/linux/mux/driver.h @@ -15,7 +15,6 @@ #include #include -#include #include struct mux_chip; @@ -44,7 +43,7 @@ struct mux_control_ops { * mux drivers. */ struct mux_control { - struct mutex lock; /* protects the state of the mux */ + struct semaphore lock; /* protects the state of the mux */ struct mux_chip *chip; int cached_state;
Re: [RFC 2/5] i3c: Add core I3C infrastructure
On 2017-07-31 23:15, Boris Brezillon wrote: > [1]https://www.mipi.org/MIPI_I3C_device_characteristics_register Stupid non-programmers... This part 65 41 0101 Accelerometer 66 42 0110 Gyroscope 67 43 0111 Magnetometer 68 44 01000100 Accel/Gyro Combo 69 45 01000101 Accel/Mag Combo 70 46 01000110 Accel/Gyro/Mag Combo should *obviously* have been something like 65 41 0101 Accelerometer 66 42 0110 Gyroscope 67 43 0111 Accel/Gyro Combo 68 44 01000100 Magnetometer 69 45 01000101 Accel/Mag Combo 71 47 01000111 Accel/Gyro/Mag Combo Cheers, peda
Re: [PATCH v2 2/3] usb: chipidea: Hook into mux framework to toggle usb switch
On 2017-08-08 03:51, Stephen Boyd wrote: > Quoting Peter Rosin (2017-07-31 03:33:22) >> On 2017-07-14 23:40, Stephen Boyd wrote: >>> @@ -1964,16 +1965,26 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) >>> >>> static int udc_id_switch_for_device(struct ci_hdrc *ci) >>> { >>> + int ret = 0; >>> + >>> if (ci->is_otg) >>> /* Clear and enable BSV irq */ >>> hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, >>> OTGSC_BSVIS | OTGSC_BSVIE); >>> >>> - return 0; >>> + if (!ci_otg_is_fsm_mode(ci)) >>> + ret = mux_control_select(ci->platdata->usb_switch, 0); >>> + >>> + if (ci->is_otg && ret) >>> + hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); >>> + >>> + return ret; >>> } >>> >>> static void udc_id_switch_for_host(struct ci_hdrc *ci) >>> { >>> + mux_control_deselect(ci->platdata->usb_switch); >>> + >> >> This looks broken. You conditionally lock the mux and you unconditionally >> unlock it. Quoting the mux_control_deselect doc: >> >> * It is required that a single call is made to mux_control_deselect() for >> * each and every successful call made to either of mux_control_select() or >> * mux_control_try_select(). >> >> Think of the mux as a semaphore with a max count of one. If you lock it, >> you have to unlock it when you're done. If you didn't lock it, you have >> zero business unlocking it. If you try to lock it but fail, you also have >> no business unlocking it. Just like a semaphore. > > Good catch. I've added a if (!ci_otg_is_fsm_mode()) check here. > >> >> Another point: I do not know if udc_id_switch_for_host is *only* called >> when there has been a prior call to udc_id_switch_for_device that >> succeeded or how this works exactly. But this all looks fragile. Using >> mux_control_select and mux_control_deselect *must* be done in pairs. >> If you want a mux to be locked for "a while", such as in this case, you >> have to make sure you stay within the rules. There is no room for half >> measures, or you will likely cause a deadlock when something unexpected >> happens. >> > > Can you elaborate? Is it bad that we keep it "locked" while we're in > host or device mode? No no, that's good. You do not want some other consumer of the same mux controller to clobber your state (in case it's shared), so you absolutely want to have the mux locked when you want it to remain in a specific position. > It looked like we paired the start/stop ops with > each other so that the mux is properly managed across these ops. Yes, it *looks* ok... > My > testing hasn't shown a problem, but maybe there's some corner case > you're thinking of? I'll double check the code. ...but since I do not know the usb code, I can't tell. What I worry about is the usb core calling udc_id_switch_for_host or udc_id_switch_for_device more than once without any call to the other in between. Maybe that is a guarantee that the usb core makes? Or maybe it isn't? If e.g. there is a third mode (or if one is added in the future), then the calls to mux_control_select and mux_control_deselect would not be paired correctly. Ok, sure, a third mode probably doesn't exist and will probably not be added, but but but... Also, what happens if udc_id_switch_for_device fails? Is it certain that it will be called again before udc_id_switch_for_host is called, or is the failure simply logged? If the latter, you might have a call to mux_control_deselect without a preceding (and successful) call to mux_control_select. That's fatal. I have similar worries for host_start/host_stop, but for that case host_stop is not allowed to fail, and it seems like a safe bet that host_stop will only be called if host_start succeeds. So, I'm not as worried there. In other words, the question is if the usb core is designed to allow this kind of "raw" resource administration in udc_id_switch_for_host and udc_id_switch_for_device, or if you need to keep a local record of the state so that you do not do double resource acquisition or attempt to free resources you don't have? I think I would feel better if the muxing for the device mode could be done in a start/stop pair of function just like the host mode is doing. Again, I don't know the usb code and don't know if such hooks exist or not? Cheers, Peter
Re: [RESEND PATCH] staging: vboxvideo: remove dead gamma lut code
On 2017-08-07 11:21, Daniel Vetter wrote: > On Fri, Aug 04, 2017 at 12:45:06PM +0200, Peter Rosin wrote: >> The redundant fb helpers .load_lut, .gamma_set and .gamma_get are >> no longer used. Remove the dead code that was not doing anything >> sensible anyway. >> >> Signed-off-by: Peter Rosin <p...@axentia.se> >> --- >> drivers/staging/vboxvideo/vbox_fb.c | 15 --- >> drivers/staging/vboxvideo/vbox_mode.c | 5 - >> 2 files changed, 20 deletions(-) >> >> [This time with an improved Cc list, sorry for the noise. For new >> people, please refer to https://lkml.org/lkml/2017/7/13/593 for >> context] >> >> Hi Daniel, >> >> Here it goes, but do you really need me to resend v5 14/14? > > Well it's not yet on dri-devel, but our pre-merge CI bots can't read such > instructions. They expect a clean new series that applies cleanly, as a > top-post, when resending stuff. Ok, noted for the next time. These things seem to vary from subsystem to subsystem, so it's not trivial to get it right w/o investing serious time looking things up. Anyway, sorry about the trouble. > Anyway, applied. Thanks! Cheers, Peter
Re: [PATCH 3/3] [media] cx231xx: only unregister successfully registered i2c adapters
On 2017-08-09 16:27, Mauro Carvalho Chehab wrote: > Em Mon, 31 Jul 2017 15:38:52 +0200 > Peter Rosin <p...@axentia.se> escreveu: > >> This prevents potentially scary debug messages from the i2c core. >> >> Signed-off-by: Peter Rosin <p...@axentia.se> >> --- >> drivers/media/usb/cx231xx/cx231xx-core.c | 3 +++ >> drivers/media/usb/cx231xx/cx231xx-i2c.c | 3 ++- >> 2 files changed, 5 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c >> b/drivers/media/usb/cx231xx/cx231xx-core.c >> index 46646ecd2dbc..f372ad3917a8 100644 >> --- a/drivers/media/usb/cx231xx/cx231xx-core.c >> +++ b/drivers/media/usb/cx231xx/cx231xx-core.c >> @@ -1311,6 +1311,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[0].i2c_period = I2C_SPEED_100K;/* 100 KHz */ >> dev->i2c_bus[0].i2c_nostop = 0; >> dev->i2c_bus[0].i2c_reserve = 0; >> +dev->i2c_bus[0].i2c_rc = -ENODEV; >> >> /* External Master 2 Bus */ >> dev->i2c_bus[1].nr = 1; >> @@ -1318,6 +1319,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[1].i2c_period = I2C_SPEED_100K;/* 100 KHz */ >> dev->i2c_bus[1].i2c_nostop = 0; >> dev->i2c_bus[1].i2c_reserve = 0; >> +dev->i2c_bus[1].i2c_rc = -ENODEV; >> >> /* Internal Master 3 Bus */ >> dev->i2c_bus[2].nr = 2; >> @@ -1325,6 +1327,7 @@ int cx231xx_dev_init(struct cx231xx *dev) >> dev->i2c_bus[2].i2c_period = I2C_SPEED_100K;/* 100kHz */ >> dev->i2c_bus[2].i2c_nostop = 0; >> dev->i2c_bus[2].i2c_reserve = 0; >> +dev->i2c_bus[2].i2c_rc = -ENODEV; >> >> /* register I2C buses */ >> errCode = cx231xx_i2c_register(>i2c_bus[0]); >> diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c >> b/drivers/media/usb/cx231xx/cx231xx-i2c.c >> index 3e49517cb5e0..8ce6b815d16d 100644 >> --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c >> +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c >> @@ -553,7 +553,8 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) >> */ >> void cx231xx_i2c_unregister(struct cx231xx_i2c *bus) >> { >> -i2c_del_adapter(>i2c_adap); >> +if (!bus->i2c_rc) >> +i2c_del_adapter(>i2c_adap); > > That doesn't sound right. what happens if i2c_rc is 1 or 2? > > IMHO, the right would would be, instead: > > if (bus->i2c_rc >= 0) > i2c_del_adapter(>i2c_adap); In theory, yes. But in practice i2c_add_adapter never returns >0, and is also documented so. Let me know if you still want an update. In that case I'll also fix the precedent present in the context of patch 1/3, i.e. if (0 != bus->i2c_rc) ... Cheers, peda
Re: [PATCH v3 0/5] iio: srf08: add support for similar devices and triggered buffers
On 2017-08-16 21:32, Andreas Klinger wrote: > This patch series adds support for: > - triggered buffer > - ultrasonic devices srf02 and srf10 > > Thanks for the review of Peter and Wolfram. > > @Wolfram: please let me know, if you want a separate patch for [PATCH 1/5] I think what you should do is rebase to something fresher, such as the togreg branch in the iio repo. As you have discovered, master branch is lagging behind in that repo. And I think Jonathan would like that as well, but perhaps it doesn't make a difference for him? Cheers, peda
[PATCH v15 02/13] dt-bindings: document devicetree bindings for mux-controllers and gpio-mux
From: Peter Rosin <p...@axentia.se> Allow specifying that a single multiplexer controller can be used to control several parallel multiplexers, thus enabling sharing of the multiplexer controller by different consumers. Add a binding for a first mux controller in the form of a GPIO based mux controller. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/devicetree/bindings/mux/gpio-mux.txt | 69 + .../devicetree/bindings/mux/mux-controller.txt | 157 + MAINTAINERS| 6 + include/dt-bindings/mux/mux.h | 16 +++ 4 files changed, 248 insertions(+) create mode 100644 Documentation/devicetree/bindings/mux/gpio-mux.txt create mode 100644 Documentation/devicetree/bindings/mux/mux-controller.txt create mode 100644 include/dt-bindings/mux/mux.h diff --git a/Documentation/devicetree/bindings/mux/gpio-mux.txt b/Documentation/devicetree/bindings/mux/gpio-mux.txt new file mode 100644 index ..b8f746344d80 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/gpio-mux.txt @@ -0,0 +1,69 @@ +GPIO-based multiplexer controller bindings + +Define what GPIO pins are used to control a multiplexer. Or several +multiplexers, if the same pins control more than one multiplexer. + +Required properties: +- compatible : "gpio-mux" +- mux-gpios : list of gpios used to control the multiplexer, least + significant bit first. +- #mux-control-cells : <0> +* Standard mux-controller bindings as decribed in mux-controller.txt + +Optional properties: +- idle-state : if present, the state the mux will have when idle. The + special state MUX_IDLE_AS_IS is the default. + +The multiplexer state is defined as the number represented by the +multiplexer GPIO pins, where the first pin is the least significant +bit. An active pin is a binary 1, an inactive pin is a binary 0. + +Example: + + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync-1", "in", "out", "sync-2"; + }; + + i2c-mux { + compatible = "i2c-mux"; + i2c-parent = <>; + + mux-controls = <>; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + ssd1307: oled@3c { + /* ... */ + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + /* ... */ + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mux/mux-controller.txt b/Documentation/devicetree/bindings/mux/mux-controller.txt new file mode 100644 index ..4f47e4bd2fa0 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/mux-controller.txt @@ -0,0 +1,157 @@ +Common multiplexer controller bindings +== + +A multiplexer (or mux) controller will have one, or several, consumer devices +that uses the mux controller. Thus, a mux controller can possibly control +several parallel multiplexers. Presumably there will be at least one +multiplexer needed by each consumer, but a single mux controller can of course +control several multiplexers for a single consumer. + +A mux controller provides a number of states to its consumers, and the state +space is a simple zero-based enumeration. I.e. 0-1 for a 2-way multiplexer, +0-7 for an 8-way multiplexer, etc. + + +Consumers +- + +Mux controller consumers should specify a list of mux controllers that they +want to use with a property containing a 'mux-ctrl-list': + + mux-ctrl-list ::= [mux-ctrl-list] + single-mux-ctrl ::= [mux-ctrl-specifier] + mux-ctrl-phandle : phandle to mux controller node + mux-ctrl-specifier : array of #mux-control-cells specifying the +given mux controller (controller specific) + +Mux controller properties should be named "mux-controls". The exact meaning of +each mux controller property must
[PATCH v15 07/13] iio: multiplexer: new iio category and iio-mux driver
From: Peter Rosin <p...@axentia.se> When a multiplexer changes how an iio device behaves (for example by feeding different signals to an ADC), this driver can be used to create one virtual iio channel for each multiplexer state. Depends on the generic multiplexer subsystem. Cache any ext_info values from the parent iio channel, creating a private copy of the ext_info attributes for each multiplexer state/channel. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- MAINTAINERS | 1 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/multiplexer/Kconfig | 18 ++ drivers/iio/multiplexer/Makefile | 6 + drivers/iio/multiplexer/iio-mux.c | 459 ++ 6 files changed, 486 insertions(+) create mode 100644 drivers/iio/multiplexer/Kconfig create mode 100644 drivers/iio/multiplexer/Makefile create mode 100644 drivers/iio/multiplexer/iio-mux.c diff --git a/MAINTAINERS b/MAINTAINERS index eea8432b2df1..c89d70c29089 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6479,6 +6479,7 @@ M: Peter Rosin <p...@axentia.se> L: linux-...@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt +F: drivers/iio/multiplexer/iio-mux.c IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron <ji...@kernel.org> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index a918270d6f54..b3c8c6ef0dff 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -83,6 +83,7 @@ source "drivers/iio/humidity/Kconfig" source "drivers/iio/imu/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" +source "drivers/iio/multiplexer/Kconfig" source "drivers/iio/orientation/Kconfig" if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 33fa4026f92c..93c769cd99bf 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -28,6 +28,7 @@ obj-y += humidity/ obj-y += imu/ obj-y += light/ obj-y += magnetometer/ +obj-y += multiplexer/ obj-y += orientation/ obj-y += potentiometer/ obj-y += potentiostat/ diff --git a/drivers/iio/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig new file mode 100644 index ..735a7b0e6fd8 --- /dev/null +++ b/drivers/iio/multiplexer/Kconfig @@ -0,0 +1,18 @@ +# +# Multiplexer drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Multiplexers" + +config IIO_MUX + tristate "IIO multiplexer driver" + select MULTIPLEXER + depends on OF || COMPILE_TEST + help + Say yes here to build support for the IIO multiplexer. + + To compile this driver as a module, choose M here: the + module will be called iio-mux. + +endmenu diff --git a/drivers/iio/multiplexer/Makefile b/drivers/iio/multiplexer/Makefile new file mode 100644 index ..68be3c4abd07 --- /dev/null +++ b/drivers/iio/multiplexer/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for industrial I/O multiplexer drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_IIO_MUX) += iio-mux.o diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c new file mode 100644 index ..37ba007f8dca --- /dev/null +++ b/drivers/iio/multiplexer/iio-mux.c @@ -0,0 +1,459 @@ +/* + * IIO multiplexer driver + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mux_ext_info_cache { + char *data; + ssize_t size; +}; + +struct mux_child { + struct mux_ext_info_cache *ext_info_cache; +}; + +struct mux { + int cached_state; + struct mux_control *control; + struct iio_channel *parent; + struct iio_dev *indio_dev; + struct iio_chan_spec *chan; + struct iio_chan_spec_ext_info *ext_info; + struct mux_child *child; +}; + +static int iio_mux_select(struct mux *mux, int idx) +{ + struct mux_child *child = >child[idx]; + struct iio_chan_spec const *chan = >chan[idx]; + int ret; + int i; + + ret = mux_control_select(mux->control, chan->channel); + if (ret < 0) { + mux->cached_state = -1; + return ret; + } + + if (mux->cached_state == chan->channel) + return 0; + + if (chan->ext_info) { + for (i = 0; chan->ext_info[i].name; ++i) { +
[PATCH v15 13/13] mux: mmio-based syscon mux controller
From: Philipp Zabel <p.za...@pengutronix.de> This adds a driver for mmio-based syscon multiplexers controlled by bitfields in a syscon register range. Signed-off-by: Philipp Zabel <p.za...@pengutronix.de> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig| 13 + drivers/mux/Makefile | 1 + drivers/mux/mux-mmio.c | 141 + 3 files changed, 155 insertions(+) create mode 100644 drivers/mux/mux-mmio.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index c4d050645605..e8f1df74644c 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -43,4 +43,17 @@ config MUX_GPIO To compile the driver as a module, choose M here: the module will be called mux-gpio. +config MUX_MMIO + tristate "MMIO register bitfield-controlled Multiplexer" + depends on (OF && MFD_SYSCON) || COMPILE_TEST + help + MMIO register bitfield-controlled Multiplexer controller. + + The driver builds multiplexer controllers for bitfields in a syscon + register. For N bit wide bitfields, there will be 2^N possible + multiplexer states. + + To compile the driver as a module, choose M here: the module will + be called mux-mmio. + endif diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index b00a7d37d2fb..6bac5b0fea13 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux-core.o obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o obj-$(CONFIG_MUX_GPIO) += mux-gpio.o +obj-$(CONFIG_MUX_MMIO) += mux-mmio.o diff --git a/drivers/mux/mux-mmio.c b/drivers/mux/mux-mmio.c new file mode 100644 index ..37c1de359a70 --- /dev/null +++ b/drivers/mux/mux-mmio.c @@ -0,0 +1,141 @@ +/* + * MMIO register bitfield-controlled multiplexer driver + * + * Copyright (C) 2017 Pengutronix, Philipp Zabel <ker...@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mux_mmio_set(struct mux_control *mux, int state) +{ + struct regmap_field **fields = mux_chip_priv(mux->chip); + + return regmap_field_write(fields[mux_control_get_index(mux)], state); +} + +static const struct mux_control_ops mux_mmio_ops = { + .set = mux_mmio_set, +}; + +static const struct of_device_id mux_mmio_dt_ids[] = { + { .compatible = "mmio-mux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mux_mmio_dt_ids); + +static int mux_mmio_probe(struct platform_device *pdev) +{ + struct device *dev = >dev; + struct device_node *np = dev->of_node; + struct regmap_field **fields; + struct mux_chip *mux_chip; + struct regmap *regmap; + int num_fields; + int ret; + int i; + + regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to get regmap: %d\n", ret); + return ret; + } + + ret = of_property_count_u32_elems(np, "mux-reg-masks"); + if (ret == 0 || ret % 2) + ret = -EINVAL; + if (ret < 0) { + dev_err(dev, "mux-reg-masks property missing or invalid: %d\n", + ret); + return ret; + } + num_fields = ret / 2; + + mux_chip = devm_mux_chip_alloc(dev, num_fields, num_fields * + sizeof(*fields)); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + fields = mux_chip_priv(mux_chip); + + for (i = 0; i < num_fields; i++) { + struct mux_control *mux = _chip->mux[i]; + struct reg_field field; + s32 idle_state = MUX_IDLE_AS_IS; + u32 reg, mask; + int bits; + + ret = of_property_read_u32_index(np, "mux-reg-masks", +2 * i, ); + if (!ret) + ret = of_property_read_u32_index(np, "mux-reg-masks", +2 * i + 1, ); + if (ret < 0) { + dev_err(dev, "bitfield %d: failed to read mux-reg-masks property: %d\n", + i, ret); + return ret; + } + + field.reg = reg; + field.msb = fls(mask) - 1; + field.lsb = ffs(mask) - 1; + + if (mask != GENMASK(field.msb, field.lsb)) { + dev_err(dev, "bitfield %d: inval
[PATCH v15 09/13] i2c: i2c-mux-gpmux: new driver
From: Peter Rosin <p...@axentia.se> This is a general purpose i2c mux that uses a multiplexer controlled by the multiplexer subsystem to do the muxing. The user can select if the mux is to be mux-locked and parent-locked as described in Documentation/i2c/i2c-topology. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Wolfram Sang <w...@the-dreams.de> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/i2c/muxes/Kconfig | 13 +++ drivers/i2c/muxes/Makefile| 1 + drivers/i2c/muxes/i2c-mux-gpmux.c | 173 ++ 3 files changed, 187 insertions(+) create mode 100644 drivers/i2c/muxes/i2c-mux-gpmux.c diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 1e160fc37ecc..2c64d0e0740f 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -30,6 +30,19 @@ config I2C_MUX_GPIO This driver can also be built as a module. If so, the module will be called i2c-mux-gpio. +config I2C_MUX_GPMUX + tristate "General Purpose I2C multiplexer" + select MULTIPLEXER + depends on OF || COMPILE_TEST + help + If you say yes to this option, support will be included for a + general purpose I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which in turn is controlled + by a MUX-controller from the MUX subsystem. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-gpmux. + config I2C_MUX_LTC4306 tristate "LTC LTC4306/5 I2C multiplexer" select GPIOLIB diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index ff7618cd5312..4a67d3199877 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE)+= i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_DEMUX_PINCTRL)+= i2c-demux-pinctrl.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o +obj-$(CONFIG_I2C_MUX_GPMUX)+= i2c-mux-gpmux.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c new file mode 100644 index ..92cf5f48afe6 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -0,0 +1,173 @@ +/* + * General Purpose I2C multiplexer + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +struct mux { + struct mux_control *control; + + bool do_not_deselect; +}; + +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + int ret; + + ret = mux_control_select(mux->control, chan); + mux->do_not_deselect = ret < 0; + + return ret; +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + + if (mux->do_not_deselect) + return 0; + + return mux_control_deselect(mux->control); +} + +static struct i2c_adapter *mux_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + +static const struct of_device_id i2c_mux_of_match[] = { + { .compatible = "i2c-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_of_match); + +static int i2c_mux_probe(struct platform_device *pdev) +{ + struct device *dev = >dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct i2c_mux_core *muxc; + struct mux *mux; + struct i2c_adapter *parent; + int children; + int ret; + + if (!np) + return -ENODEV; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->control = devm_mux_control_get(dev, NULL); + if (IS_ERR(mux->control)) { + if (PTR_ERR(mux->control) != -EPROBE_DEFER) + dev_err(dev, "failed to get control-mux\n"); +
[PATCH v15 05/13] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin <p...@axentia.se> Extend the inkern api with functions for reading and writing ext_info of iio channels. Acked-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/iio/inkern.c | 60 include/linux/iio/consumer.h | 37 +++ 2 files changed, 97 insertions(+) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 7a13535dc3e9..8292ad4435ea 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -869,3 +869,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val) return ret; } EXPORT_SYMBOL_GPL(iio_write_channel_raw); + +unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) +{ + const struct iio_chan_spec_ext_info *ext_info; + unsigned int i = 0; + + if (!chan->channel->ext_info) + return i; + + for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++) + ++i; + + return i; +} +EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); + +static const struct iio_chan_spec_ext_info *iio_lookup_ext_info( + const struct iio_channel *chan, + const char *attr) +{ + const struct iio_chan_spec_ext_info *ext_info; + + if (!chan->channel->ext_info) + return NULL; + + for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) { + if (!strcmp(attr, ext_info->name)) + return ext_info; + } + + return NULL; +} + +ssize_t iio_read_channel_ext_info(struct iio_channel *chan, + const char *attr, char *buf) +{ + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = iio_lookup_ext_info(chan, attr); + if (!ext_info) + return -EINVAL; + + return ext_info->read(chan->indio_dev, ext_info->private, + chan->channel, buf); +} +EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); + +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, + const char *buf, size_t len) +{ + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = iio_lookup_ext_info(chan, attr); + if (!ext_info) + return -EINVAL; + + return ext_info->write(chan->indio_dev, ext_info->private, + chan->channel, buf, len); +} +EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 47eeec3218b5..5e347a9805fd 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -312,4 +312,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, int *processed, unsigned int scale); +/** + * iio_get_channel_ext_info_count() - get number of ext_info attributes + * connected to the channel. + * @chan: The channel being queried + * + * Returns the number of ext_info attributes + */ +unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan); + +/** + * iio_read_channel_ext_info() - read ext_info attribute from a given channel + * @chan: The channel being queried. + * @attr: The ext_info attribute to read. + * @buf: Where to store the attribute value. Assumed to hold + * at least PAGE_SIZE bytes. + * + * Returns the number of bytes written to buf (perhaps w/o zero termination; + * it need not even be a string), or an error code. + */ +ssize_t iio_read_channel_ext_info(struct iio_channel *chan, + const char *attr, char *buf); + +/** + * iio_write_channel_ext_info() - write ext_info attribute from a given channel + * @chan: The channel being queried. + * @attr: The ext_info attribute to read. + * @buf: The new attribute value. Strings needs to be zero- + * terminated, but the terminator should not be included + * in the below len. + * @len: The size of the new attribute value. + * + * Returns the number of accepted bytes, which should be the same as len. + * An error code can also be returned. + */ +ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, + const char *buf, size_t len); + #endif -- 2.1.4
[PATCH v15 08/13] dt-bindings: i2c: i2c-mux: document general purpose i2c-mux bindings
From: Peter Rosin <p...@axentia.se> Describe how a general purpose multiplexer controller is used to mux an i2c bus. Acked-by: Jonathan Cameron <ji...@kernel.org> Reviewed-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../devicetree/bindings/i2c/i2c-mux-gpmux.txt | 99 ++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt new file mode 100644 index ..2907dab56298 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.txt @@ -0,0 +1,99 @@ +General Purpose I2C Bus Mux + +This binding describes an I2C bus multiplexer that uses a mux controller +from the mux subsystem to route the I2C signals. + + .-. .-. + | dev | | dev | +..'-' '-' +| SoC| || +|| .+' +| .--. | .--+child bus A, on MUX value set to 0 +| | I2C |-|--| Mux | +| '--' | '--+---+child bus B, on MUX value set to 1 +| .--. | |'--++. +| | MUX- | | | ||| +| | Ctrl |-|-+.-. .-. .-. +| '--' | | dev | | dev | | dev | +'' '-' '-' '-' + +Required properties: +- compatible: i2c-mux +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side + port is connected to. +- mux-controls: The phandle of the mux controller to use for operating the + mux. +* Standard I2C mux properties. See i2c-mux.txt in this directory. +* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number + is also the mux-controller state described in ../mux/mux-controller.txt + +Optional properties: +- mux-locked: If present, explicitly allow unrelated I2C transactions on the + parent I2C adapter at these times: + + during setup of the multiplexer + + between setup of the multiplexer and the child bus I2C transaction + + between the child bus I2C transaction and releasing of the multiplexer + + during releasing of the multiplexer + However, I2C transactions to devices behind all I2C multiplexers connected + to the same parent adapter that this multiplexer is connected to are blocked + for the full duration of the complete multiplexed I2C transaction (i.e. + including the times covered by the above list). + If mux-locked is not present, the multiplexer is assumed to be parent-locked. + This means that no unrelated I2C transactions are allowed on the parent I2C + adapter for the complete multiplexed I2C transaction. + The properties of mux-locked and parent-locked multiplexers are discussed + in more detail in Documentation/i2c/i2c-topology. + +For each i2c child node, an I2C child bus will be created. They will +be numbered based on their order in the device tree. + +Whenever an access is made to a device on a child bus, the value set +in the relevant node's reg property will be set as the state in the +mux controller. + +Example: + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + i2c-mux { + compatible = "i2c-mux"; + mux-locked; + i2c-parent = <>; + + mux-controls = <>; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + ssd1307: oled@3c { + compatible = "solomon,ssd1307fb-i2c"; + reg = <0x3c>; + pwms = < 4 3000>; + reset-gpios = < 7 1>; + reset-active-low; + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + compatible = "nxp,pca9555"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x20>; + }; + }; + }; -- 2.1.4
[PATCH v15 12/13] dt-bindings: add mmio-based syscon mux controller DT bindings
From: Philipp Zabel <p.za...@pengutronix.de> This adds device tree binding documentation for mmio-based syscon multiplexers controlled by a bitfields in a syscon register range. Signed-off-by: Philipp Zabel <p.za...@pengutronix.de> Acked-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/devicetree/bindings/mux/mmio-mux.txt | 60 ++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/mux/mmio-mux.txt diff --git a/Documentation/devicetree/bindings/mux/mmio-mux.txt b/Documentation/devicetree/bindings/mux/mmio-mux.txt new file mode 100644 index ..a9bfb4d8b6ac --- /dev/null +++ b/Documentation/devicetree/bindings/mux/mmio-mux.txt @@ -0,0 +1,60 @@ +MMIO register bitfield-based multiplexer controller bindings + +Define register bitfields to be used to control multiplexers. The parent +device tree node must be a syscon node to provide register access. + +Required properties: +- compatible : "mmio-mux" +- #mux-control-cells : <1> +- mux-reg-masks : an array of register offset and pre-shifted bitfield mask + pairs, each describing a single mux control. +* Standard mux-controller bindings as decribed in mux-controller.txt + +Optional properties: +- idle-states : if present, the state the muxes will have when idle. The + special state MUX_IDLE_AS_IS is the default. + +The multiplexer state of each multiplexer is defined as the value of the +bitfield described by the corresponding register offset and bitfield mask pair +in the mux-reg-masks array, accessed through the parent syscon. + +Example: + + syscon { + compatible = "syscon"; + + mux: mux-controller { + compatible = "mmio-mux"; + #mux-control-cells = <1>; + + mux-reg-masks = <0x3 0x30>, /* 0: reg 0x3, bits 5:4 */ + <0x3 0x40>, /* 1: reg 0x3, bit 6 */ + idle-states = , <0>; + }; + }; + + video-mux { + compatible = "video-mux"; + mux-controls = < 0>; + + ports { + /* inputs 0..3 */ + port@0 { + reg = <0>; + }; + port@1 { + reg = <1>; + }; + port@2 { + reg = <2>; + }; + port@3 { + reg = <3>; + }; + + /* output */ + port@4 { + reg = <4>; + }; + }; + }; -- 2.1.4
[PATCH v15 00/13] mux controller abstraction and iio/i2c muxes
From: Peter Rosin <p...@axentia.se> Hi Greg, Philipp found problems in v14 with using a mutex for locking that was the outcome of the review for v13, so I'm now using a semaphore instead of the rwsem that was in v13. That at least got rid of the scary call to downgrade_write. However, I'm still unsure about what you actually meant with your comment about lack of sparse markings [1]. I did add __must_check to the funcs that selects the mux, but I've got this feeling that this is not what you meant? Anyway, I have acted on all your comments from the v13 review, at least I think I did. So, please apply. [1] https://lkml.org/lkml/2017/4/21/794 v14 -> v15 changes - Rebased onto v4.12-rc1 - Add the mmio-mux driver from Philipp Zabel to the end of the series. - Using a mutex to lock the mux state did not work out for Philipp Zabel and his video-mux, which is locking the mux over long periods of time. So, switch to a semaphore (where different tasks can call down/up). - Remove unused devm_mux_chip_free, devm_mux_chip_unregister and devm_mux_control_put. Those are currently unused and can be added when someone needs them. - Add more words in the kernel-doc comments for mux_control_select, mux_control_try_select and mux_control_deselect on the calling rules. - Add "|| COMPILE_TEST" to the Kconfig depends for all new drivers. [older changes follow below] This adds a new mux controller subsystem with an interface for accessing mux controllers, along with three drivers providing the interface (gpio, mmio and adg792) and two consumers (iio and i2c). This is done in a way that several consumers can independently access the same mux controller if one controller controls several multiplexers, thus allowing sharing. But sharing is by no means required, of course. It is perfectly fine to have a single consumer of a dedicated mux controller controlling only one mux for said consumer. The prediction is that the typical use case will be for gpio-based muxing (which is also what drove the development), where the below schematics show the flexibility with one gpio-based mux controller being shared by the iio-mux and i2c-mux-gpmux drivers. .. |GPO0|--. |GPO1|. | ||| | || .---. || |dg4052a| || | | |ADC0|-|XX0| signal X0 || | X1| signal X1 || | X2| signal X2 || | X3| signal X3 || | | |SDA0|-|YY0| i2c segment Y0 |SCL0|--. | Y1| i2c segment Y1 '' | | Y2| i2c segment Y2 | | Y3| i2c segment Y3 | '---' |0 1 2 3 (feed SCL0 to each of || | | |the 4 muxed segments) '+-+-+-' GPO0 and GPO1 may also be fed to further parallel muxers, which is perhaps desired in a real application to minimize digital noise from the I2C Y channel leaking into the analog X channel. I.e. it might be a good idea to separate the analog and digital signals... And the below hypothetical schematics indicate something similar but using the I2C-based adg792a multiplexer instead. .. |SDA0|--. |SCL0|. | ||| | || .---. || |adg792a| || | | |ADC0|-|D1 S1A| signal S1A || |S1B| signal S1B || |S1C| signal S1C || |S1D| signal S1D || | | |SDA1|---+-|D2 S2A| i2c segment S2A |SCL1|-. | |S2B| i2c segment S2B '' | | |S2C| i2c segment S2C | | |S2D| i2c segment S2D | | | | | '-|D3 S3A| i2c segment S3A | |S3B| i2c segment S3B | |S3C| i2c segment S3C | |S3D| i2c segment S3D | '---' | A B C D A B C D (feed SCL1 to each of | | | | | | | | | the 8 muxed segments) '-+-+-+-+---+-+-+-' Background: I have a piece of hardware that is using the same 3 GPIO pins to control four 8-way muxes. Three of them control ADC lines to an ADS1015 chip with an iio driver, and the last one controls the SDA line of an i2c bus. We have some deployed code to handle this, but you do not want to see it or ever hear about it. I'm not sure why I even mention it. Anyway, the situation has nagged me to no end for quite some time. So, after first getting more intimate with the i2c muxing code and later discovering the drivers/iio/inkern.c file and writing a couple of drivers making use of it, I came up with what I think is an acceptable solution; add a generic mux controller driver (and subsystem) that is shared between all instances, and combine that with an iio mux driver and a new
[PATCH v15 10/13] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
From: Peter Rosin <p...@axentia.se> Analog Devices ADG792A/G is a triple 4:1 mux. Acked-by: Jonathan Cameron <ji...@kernel.org> Reviewed-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../devicetree/bindings/mux/adi,adg792a.txt| 75 ++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/mux/adi,adg792a.txt diff --git a/Documentation/devicetree/bindings/mux/adi,adg792a.txt b/Documentation/devicetree/bindings/mux/adi,adg792a.txt new file mode 100644 index ..96b787a69f50 --- /dev/null +++ b/Documentation/devicetree/bindings/mux/adi,adg792a.txt @@ -0,0 +1,75 @@ +Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers + +Required properties: +- compatible : "adi,adg792a" or "adi,adg792g" +- #mux-control-cells : <0> if parallel (the three muxes are bound together + with a single mux controller controlling all three muxes), or <1> if + not (one mux controller for each mux). +* Standard mux-controller bindings as described in mux-controller.txt + +Optional properties for ADG792G: +- gpio-controller : if present, #gpio-cells below is required. +- #gpio-cells : should be <2> + - First cell is the GPO line number, i.e. 0 or 1 + - Second cell is used to specify active high (0) + or active low (1) + +Optional properties: +- idle-state : if present, array of states that the mux controllers will have + when idle. The special state MUX_IDLE_AS_IS is the default and + MUX_IDLE_DISCONNECT is also supported. + +States 0 through 3 correspond to signals A through D in the datasheet. + +Example: + + /* +* Three independent mux controllers (of which one is used). +* Mux 0 is disconnected when idle, mux 1 idles in the previously +* selected state and mux 2 idles with signal B. +*/ +{ + mux: mux-controller@50 { + compatible = "adi,adg792a"; + reg = <0x50>; + #mux-control-cells = <1>; + + idle-state = ; + }; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = < 2>; + + channels = "sync-1", "", "out"; + }; + + + /* +* Three parallel muxes with one mux controller, useful e.g. if +* the adc is differential, thus needing two signals to be muxed +* simultaneously for correct operation. +*/ +{ + pmux: mux-controller@50 { + compatible = "adi,adg792a"; + reg = <0x50>; + #mux-control-cells = <0>; + + idle-state = <1>; + }; + }; + + diff-adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync-1", "", "out"; + }; -- 2.1.4
[PATCH v15 01/13] devres: trivial whitespace fix
From: Peter Rosin <p...@axentia.se> Everything else is indented with two spaces, so fix the odd one out. Acked-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/driver-model/devres.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index e72587fe477d..af08b4cd7968 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -335,7 +335,7 @@ MEM devm_kzalloc() MFD - devm_mfd_add_devices() + devm_mfd_add_devices() PER-CPU MEM devm_alloc_percpu() -- 2.1.4
[PATCH v15 03/13] mux: minimal mux subsystem
From: Peter Rosin <p...@axentia.se> Add a new minimalistic subsystem that handles multiplexer controllers. When multiplexers are used in various places in the kernel, and the same multiplexer controller can be used for several independent things, there should be one place to implement support for said multiplexer controller. A single multiplexer controller can also be used to control several parallel multiplexers, that are in turn used by different subsystems in the kernel, leading to a need to coordinate multiplexer accesses. The multiplexer subsystem handles this coordination. Thanks go out to Lars-Peter Clausen, Jonathan Cameron, Rob Herring, Wolfram Sang, Paul Gortmaker, Dan Carpenter, Colin Ian King, Greg Kroah-Hartman and last but certainly not least to Philipp Zabel for helpful comments, reviews, patches and general encouragement! Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- Documentation/ABI/testing/sysfs-class-mux | 16 + Documentation/driver-model/devres.txt | 5 + MAINTAINERS | 3 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/mux/Kconfig | 16 + drivers/mux/Makefile | 5 + drivers/mux/mux-core.c| 547 ++ include/linux/mux/consumer.h | 32 ++ include/linux/mux/driver.h| 108 ++ 10 files changed, 735 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-mux create mode 100644 drivers/mux/Kconfig create mode 100644 drivers/mux/Makefile create mode 100644 drivers/mux/mux-core.c create mode 100644 include/linux/mux/consumer.h create mode 100644 include/linux/mux/driver.h diff --git a/Documentation/ABI/testing/sysfs-class-mux b/Documentation/ABI/testing/sysfs-class-mux new file mode 100644 index ..8715f9c7bd4f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mux @@ -0,0 +1,16 @@ +What: /sys/class/mux/ +Date: April 2017 +KernelVersion: 4.13 +Contact: Peter Rosin <p...@axentia.se> +Description: + The mux/ class sub-directory belongs to the Generic MUX + Framework and provides a sysfs interface for using MUX + controllers. + +What: /sys/class/mux/muxchipN/ +Date: April 2017 +KernelVersion: 4.13 +Contact: Peter Rosin <p...@axentia.se> +Description: + A /sys/class/mux/muxchipN directory is created for each + probed MUX chip where N is a simple enumeration. diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index af08b4cd7968..40e4787c399c 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -337,6 +337,11 @@ MEM MFD devm_mfd_add_devices() +MUX + devm_mux_chip_alloc() + devm_mux_chip_register() + devm_mux_control_get() + PER-CPU MEM devm_alloc_percpu() devm_free_percpu() diff --git a/MAINTAINERS b/MAINTAINERS index e17dd0bf2c9f..a0cce325b08e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8712,8 +8712,11 @@ F: include/linux/spi/mmc_spi.h MULTIPLEXER SUBSYSTEM M: Peter Rosin <p...@axentia.se> S: Maintained +F: Documentation/ABI/testing/mux/sysfs-class-mux* F: Documentation/devicetree/bindings/mux/ F: include/linux/dt-bindings/mux/ +F: include/linux/mux/ +F: drivers/mux/ MULTISOUND SOUND DRIVER M: Andrew Veliath <andre...@usa.net> diff --git a/drivers/Kconfig b/drivers/Kconfig index ba2901e76769..505c676fa9c7 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -206,4 +206,6 @@ source "drivers/fsi/Kconfig" source "drivers/tee/Kconfig" +source "drivers/mux/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index cfabd141dba2..dfdcda00bfe3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -181,3 +181,4 @@ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_MULTIPLEXER) += mux/ diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig new file mode 100644 index ..23ab2cde83b1 --- /dev/null +++ b/drivers/mux/Kconfig @@ -0,0 +1,16 @@ +# +# Multiplexer devices +# + +menuconfig MULTIPLEXER + tristate "Multiplexer subsystem" + help + Multiplexer controller subsystem. Multiplexers are used in a + variety of settings, and this subsystem abstracts their use + so that the rest of the kernel sees a common interface. When + multiple parallel multiplexers are controlled by one single + multiplexer controller, this subsystem also coordinates the + multiplexer accesses. + + To compile the subsystem as a module, choose M her
[PATCH v15 04/13] mux: gpio: add mux controller driver for gpio based multiplexers
From: Peter Rosin <p...@axentia.se> The driver builds a single multiplexer controller using a number of gpio pins. For N pins, there will be 2^N possible multiplexer states. The GPIO pins can be connected (by the hardware) to several multiplexers, which in that case will be operated in parallel. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig| 18 drivers/mux/Makefile | 1 + drivers/mux/mux-gpio.c | 114 + 3 files changed, 133 insertions(+) create mode 100644 drivers/mux/mux-gpio.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 23ab2cde83b1..738670aaecb7 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -14,3 +14,21 @@ menuconfig MULTIPLEXER To compile the subsystem as a module, choose M here: the module will be called mux-core. + +if MULTIPLEXER + +config MUX_GPIO + tristate "GPIO-controlled Multiplexer" + depends on GPIOLIB || COMPILE_TEST + help + GPIO-controlled Multiplexer controller. + + The driver builds a single multiplexer controller using a number + of gpio pins. For N pins, there will be 2^N possible multiplexer + states. The GPIO pins can be connected (by the hardware) to several + multiplexers, which in that case will be operated in parallel. + + To compile the driver as a module, choose M here: the module will + be called mux-gpio. + +endif diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 09f0299e109d..bb16953f6290 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_MULTIPLEXER) += mux-core.o +obj-$(CONFIG_MUX_GPIO) += mux-gpio.o diff --git a/drivers/mux/mux-gpio.c b/drivers/mux/mux-gpio.c new file mode 100644 index ..468bf1709606 --- /dev/null +++ b/drivers/mux/mux-gpio.c @@ -0,0 +1,114 @@ +/* + * GPIO-controlled multiplexer driver + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct mux_gpio { + struct gpio_descs *gpios; + int *val; +}; + +static int mux_gpio_set(struct mux_control *mux, int state) +{ + struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip); + int i; + + for (i = 0; i < mux_gpio->gpios->ndescs; i++) + mux_gpio->val[i] = (state >> i) & 1; + + gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs, + mux_gpio->gpios->desc, + mux_gpio->val); + + return 0; +} + +static const struct mux_control_ops mux_gpio_ops = { + .set = mux_gpio_set, +}; + +static const struct of_device_id mux_gpio_dt_ids[] = { + { .compatible = "gpio-mux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids); + +static int mux_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = >dev; + struct mux_chip *mux_chip; + struct mux_gpio *mux_gpio; + int pins; + s32 idle_state; + int ret; + + pins = gpiod_count(dev, "mux"); + if (pins < 0) + return pins; + + mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) + + pins * sizeof(*mux_gpio->val)); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + mux_gpio = mux_chip_priv(mux_chip); + mux_gpio->val = (int *)(mux_gpio + 1); + mux_chip->ops = _gpio_ops; + + mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW); + if (IS_ERR(mux_gpio->gpios)) { + ret = PTR_ERR(mux_gpio->gpios); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get gpios\n"); + return ret; + } + WARN_ON(pins != mux_gpio->gpios->ndescs); + mux_chip->mux->states = 1 << pins; + + ret = device_property_read_u32(dev, "idle-state", (u32 *)_state); + if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) { + if (idle_state < 0 || idle_state >= mux_chip->mux->states) { + dev_err(dev, "invalid idle-state %u\n", idle_state); + return -EINVAL; + } + + mux_chip->mux->idle_state = idle_state; + } + + ret = devm_mux_chip_register(dev, mux_chip); + if (ret < 0) + return ret; + + dev_info(dev,
[PATCH v15 11/13] mux: adg792a: add mux controller driver for ADG792A/G
From: Peter Rosin <p...@axentia.se> Analog Devices ADG792A/G is a triple 4:1 mux. Reviewed-by: Jonathan Cameron <ji...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/mux/Kconfig | 12 drivers/mux/Makefile | 1 + drivers/mux/mux-adg792a.c | 157 ++ 3 files changed, 170 insertions(+) create mode 100644 drivers/mux/mux-adg792a.c diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 738670aaecb7..c4d050645605 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -17,6 +17,18 @@ menuconfig MULTIPLEXER if MULTIPLEXER +config MUX_ADG792A + tristate "Analog Devices ADG792A/ADG792G Multiplexers" + depends on I2C || COMPILE_TEST + help + ADG792A and ADG792G Wide Bandwidth Triple 4:1 Multiplexers + + The driver supports both operating the three multiplexers in + parallel and operating them independently. + + To compile the driver as a module, choose M here: the module will + be called mux-adg792a. + config MUX_GPIO tristate "GPIO-controlled Multiplexer" depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index bb16953f6290..b00a7d37d2fb 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_MULTIPLEXER) += mux-core.o +obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o obj-$(CONFIG_MUX_GPIO) += mux-gpio.o diff --git a/drivers/mux/mux-adg792a.c b/drivers/mux/mux-adg792a.c new file mode 100644 index ..12aa221ab90d --- /dev/null +++ b/drivers/mux/mux-adg792a.c @@ -0,0 +1,157 @@ +/* + * Multiplexer driver for Analog Devices ADG792A/G Triple 4:1 mux + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <p...@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define ADG792A_LDSW BIT(0) +#define ADG792A_RESETB BIT(1) +#define ADG792A_DISABLE(mux) (0x50 | (mux)) +#define ADG792A_DISABLE_ALL(0x5f) +#define ADG792A_MUX(mux, state)(0xc0 | (((mux) + 1) << 2) | (state)) +#define ADG792A_MUX_ALL(state) (0xc0 | (state)) + +static int adg792a_write_cmd(struct i2c_client *i2c, u8 cmd, int reset) +{ + u8 data = ADG792A_RESETB | ADG792A_LDSW; + + /* ADG792A_RESETB is active low, the chip resets when it is zero. */ + if (reset) + data &= ~ADG792A_RESETB; + + return i2c_smbus_write_byte_data(i2c, cmd, data); +} + +static int adg792a_set(struct mux_control *mux, int state) +{ + struct i2c_client *i2c = to_i2c_client(mux->chip->dev.parent); + u8 cmd; + + if (mux->chip->controllers == 1) { + /* parallel mux controller operation */ + if (state == MUX_IDLE_DISCONNECT) + cmd = ADG792A_DISABLE_ALL; + else + cmd = ADG792A_MUX_ALL(state); + } else { + unsigned int controller = mux_control_get_index(mux); + + if (state == MUX_IDLE_DISCONNECT) + cmd = ADG792A_DISABLE(controller); + else + cmd = ADG792A_MUX(controller, state); + } + + return adg792a_write_cmd(i2c, cmd, 0); +} + +static const struct mux_control_ops adg792a_ops = { + .set = adg792a_set, +}; + +static int adg792a_probe(struct i2c_client *i2c, +const struct i2c_device_id *id) +{ + struct device *dev = >dev; + struct mux_chip *mux_chip; + s32 idle_state[3]; + u32 cells; + int ret; + int i; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + ret = device_property_read_u32(dev, "#mux-control-cells", ); + if (ret < 0) + return ret; + if (cells >= 2) + return -EINVAL; + + mux_chip = devm_mux_chip_alloc(dev, cells ? 3 : 1, 0); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + mux_chip->ops = _ops; + + ret = adg792a_write_cmd(i2c, ADG792A_DISABLE_ALL, 1); + if (ret < 0) + return ret; + + ret = device_property_read_u32_array(dev, "idle-state", +(u32 *)idle_state, +mux_chip->controllers); + if (ret < 0) { + idle_state[0] = MUX_IDLE_AS_IS; + idle_state[1] = MUX_IDLE_AS_IS; + idle_state[2] = MUX_IDLE_AS_IS; + } + + for (i = 0; i < mux_chip->controllers; ++i) { + struct mux_control *mux
[PATCH v15 06/13] dt-bindings: iio: io-channel-mux: document io-channel-mux bindings
From: Peter Rosin <p...@axentia.se> Describe how a multiplexer can be used to select which signal is fed to an io-channel. Acked-by: Jonathan Cameron <ji...@kernel.org> Acked-by: Rob Herring <r...@kernel.org> Signed-off-by: Peter Rosin <p...@axentia.se> --- .../bindings/iio/multiplexer/io-channel-mux.txt| 39 ++ MAINTAINERS| 6 2 files changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt new file mode 100644 index ..c82794002595 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt @@ -0,0 +1,39 @@ +I/O channel multiplexer bindings + +If a multiplexer is used to select which hardware signal is fed to +e.g. an ADC channel, these bindings describe that situation. + +Required properties: +- compatible : "io-channel-mux" +- io-channels : Channel node of the parent channel that has multiplexed + input. +- io-channel-names : Should be "parent". +- #address-cells = <1>; +- #size-cells = <0>; +- mux-controls : Mux controller node to use for operating the mux +- channels : List of strings, labeling the mux controller states. + +For each non-empty string in the channels property, an io-channel will +be created. The number of this io-channel is the same as the index into +the list of strings in the channels property, and also matches the mux +controller state. The mux controller state is described in +../mux/mux-controller.txt + +Example: + mux: mux-controller { + compatible = "mux-gpio"; + #mux-control-cells = <0>; + + mux-gpios = < 0 GPIO_ACTIVE_HIGH>, + < 1 GPIO_ACTIVE_HIGH>; + }; + + adc-mux { + compatible = "io-channel-mux"; + io-channels = < 0>; + io-channel-names = "parent"; + + mux-controls = <>; + + channels = "sync", "in", "system-regulator"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a0cce325b08e..eea8432b2df1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6474,6 +6474,12 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt F: drivers/iio/adc/envelope-detector.c +IIO MULTIPLEXER +M: Peter Rosin <p...@axentia.se> +L: linux-...@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt + IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron <ji...@kernel.org> R: Hartmut Knaack <knaac...@gmx.de> -- 2.1.4
[PATCH] i2c: mux: only print failure message on error
As is, a failure message is printed unconditionally, which is confusing. And noisy. Fixes: 8d4d159f25a7 ("i2c: mux: provide more info on failure in i2c_mux_add_adapter") Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/i2c/i2c-mux.c | 24 ++-- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 26f7237558ba..ccf2ce1836c8 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -395,18 +395,22 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, if (force_nr) { priv->adap.nr = force_nr; ret = i2c_add_numbered_adapter(>adap); - dev_err(>dev, - "failed to add mux-adapter %u as bus %u (error=%d)\n", - chan_id, force_nr, ret); + if (ret < 0) { + dev_err(>dev, + "failed to add mux-adapter %u as bus %u (error=%d)\n", + chan_id, force_nr, ret); + kfree(priv); + return ret; + } } else { ret = i2c_add_adapter(>adap); - dev_err(>dev, - "failed to add mux-adapter %u (error=%d)\n", - chan_id, ret); - } - if (ret < 0) { - kfree(priv); - return ret; + if (ret < 0) { + dev_err(>dev, + "failed to add mux-adapter %u (error=%d)\n", + chan_id, ret); + kfree(priv); + return ret; + } } WARN(sysfs_create_link(>adap.dev.kobj, >dev->kobj, -- 2.1.4
[PATCH v2] i2c: mux: only print failure message on error
As is, a failure message is printed unconditionally, which is confusing. And noisy. Fixes: 8d4d159f25a7 ("i2c: mux: provide more info on failure in i2c_mux_add_adapter") Signed-off-by: Peter Rosin <p...@axentia.se> --- drivers/i2c/i2c-mux.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) Wolfram, you can take this one directly if you wish. You can also take [1] (and optionally [2]) directly if you wish. Or just holler and I'll send you a pull request with [1] and this patch for-current. [1] i2c: mux: reg: put away the parent i2c adapter on probe failure https://patchwork.ozlabs.org/patch/759487/ [2] i2c: mux: reg: rename label to indicate what it does https://patchwork.ozlabs.org/patch/759486/ Changes since v1: - Use goto instead of having two kfree, as pointed out by Leon Romanovsky. Cheers, peda diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 26f7237558ba..9669ca4937b8 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -395,18 +395,20 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, if (force_nr) { priv->adap.nr = force_nr; ret = i2c_add_numbered_adapter(>adap); - dev_err(>dev, - "failed to add mux-adapter %u as bus %u (error=%d)\n", - chan_id, force_nr, ret); + if (ret < 0) { + dev_err(>dev, + "failed to add mux-adapter %u as bus %u (error=%d)\n", + chan_id, force_nr, ret); + goto err_free_priv; + } } else { ret = i2c_add_adapter(>adap); - dev_err(>dev, - "failed to add mux-adapter %u (error=%d)\n", - chan_id, ret); - } - if (ret < 0) { - kfree(priv); - return ret; + if (ret < 0) { + dev_err(>dev, + "failed to add mux-adapter %u (error=%d)\n", + chan_id, ret); + goto err_free_priv; + } } WARN(sysfs_create_link(>adap.dev.kobj, >dev->kobj, @@ -422,6 +424,10 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, muxc->adapter[muxc->num_adapters++] = >adap; return 0; + +err_free_priv: + kfree(priv); + return ret; } EXPORT_SYMBOL_GPL(i2c_mux_add_adapter); -- 2.1.4
Re: [PATCH] i2c: mux: only print failure message on error
On 2017-05-15 06:53, Leon Romanovsky wrote: > On Sun, May 14, 2017 at 06:41:13PM +0200, Peter Rosin wrote: >> As is, a failure message is printed unconditionally, which is confusing. >> And noisy. >> >> Fixes: 8d4d159f25a7 ("i2c: mux: provide more info on failure in >> i2c_mux_add_adapter") >> Signed-off-by: Peter Rosin <p...@axentia.se> >> --- >> drivers/i2c/i2c-mux.c | 24 ++-- >> 1 file changed, 14 insertions(+), 10 deletions(-) >> >> diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c >> index 26f7237558ba..ccf2ce1836c8 100644 >> --- a/drivers/i2c/i2c-mux.c >> +++ b/drivers/i2c/i2c-mux.c >> @@ -395,18 +395,22 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, >> if (force_nr) { >> priv->adap.nr = force_nr; >> ret = i2c_add_numbered_adapter(>adap); >> -dev_err(>dev, >> -"failed to add mux-adapter %u as bus %u (error=%d)\n", >> -chan_id, force_nr, ret); >> +if (ret < 0) { >> +dev_err(>dev, >> +"failed to add mux-adapter %u as bus %u >> (error=%d)\n", >> +chan_id, force_nr, ret); >> +kfree(priv); >> +return ret; >> +} >> } else { >> ret = i2c_add_adapter(>adap); >> -dev_err(>dev, >> -"failed to add mux-adapter %u (error=%d)\n", >> -chan_id, ret); >> -} >> -if (ret < 0) { >> -kfree(priv); >> -return ret; >> +if (ret < 0) { >> +dev_err(>dev, >> +"failed to add mux-adapter %u (error=%d)\n", >> +chan_id, ret); >> +kfree(priv); >> +return ret; > > It is better to add goto label, this will give one place for kfree->return > code. I guess that can be argued, but it's going to end up being more lines. Whatever, I'll just repaint the shed. v2 coming up. Cheers, peda
Re: [PATCH v2 2/3] iio: adc: at91-sama5d2_adc: add hw trigger and buffer support
On 2017-05-16 20:03, Jonathan Cameron wrote: > As we are only left with one area of questions. >>> >>> +static const struct at91_adc_trigger at91_adc_trigger_list[] = { >>> +{ >>> +.name = "external-rising", >>> +.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE, >>> +}, >>> +{ >>> +.name = "external-falling", >>> +.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL, >>> +}, >>> +{ >>> +.name = "external-any", >>> +.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY, >>> +}, >> Hmm. Should this be a userspace configurable option? Feels rather like >> it is an element of the hardware - reflecting the characteristics of >> some >> hardware device sat on the pin. > The user can choose from sysfs which trigger > is best suited for the use case, since all > three triggers are provided and can be connected to the buffer. > It reflects more the triggering capability of the ADC rather than > any different hardware device sitting on the pin I am also in favour of a userspace configurable option. For sure it's hardware related but on our board we only provide a trigger pin, we don't know which hardware the customer will put on this pin. >>> hmm. OK I'm persuaded I think. >>> >>> Could do this with devicetree overlays or similar. >>> >>> So follow up question is whether this is the right interface. >>> Can all 3 run at once sensibly? If not are we not looking at a control >>> parameter for a single trigger? >> >> There is a single trigger hardware pin, but can work in one of the three >> modes. Do you suggest I should change it to a single trigger, but then >> we need some kind of sysfs entry to control it ? >> >> Or perhaps change the trigger to be exclusive to the device/buffer, in >> which case just one trigger can be used anyway with the current_trigger >> option in buffer. >> >> If somehow more than one trigger would be enabled in the same time, >> only the last enabled one will work, since I write the corresponding >> trigger edge configuration value in the ADC register. So in fact these three >> triggers are mutually exclusive, as enabling one disables the >> other, however, it's not transparent to the user. > Ah that definitely suggests to me it should be a sysfs attribute associated > with the trigger rather than 3 separate triggers. The interpretation > of those triggers would require userspace to have some knowledge of what > is going on, so I don't think we have any problems by requiring it instead > to know about a sysfs attribute. >> >> These kind of different edge triggers used to be in device tree in >> older at91 driver. >> I have given it thought and decided to let just the driver know about >> them. Since they are bound to the ADC IP block and not related to any >> hardware description of the board or the special connectivity that >> the driver might be interested about. I mean the driver knows the >> trigger works this way because it's part of the block, and device tree >> cannot change this, so the portability of the driver is not affected >> and related to SoC design only. > Sort of (I think..) As I understand it we are still talking about an > external pin? A possible usecase for this sort of thing would be > combining with an external sequencer with an analog mux. In that > case only one of the options will make any sense. (this is the > hardware equivalent of what Peter Rosin's mux subsystem puts > under software control) > > We might be in one of those interesting cases where a devicetree > binding should exist for the fixed case, but with the flexibility > to allow userspace to specify it if and only if it is not specified > in the device tree. So if the devicetree in some sense describes > downstream hardware it is fixed as appropriate. If it doesn't and > we are looking at an 'edge of known world' situation then we let > userspace have control? Does that make sense? > > I don't suppose we have any idea what this is actually used for > on real world boards? > > I've pulled in Peter, Mark and Rob as CCs to see if they want to > comment. >From what I can tell, this touches the mux code [1] for cases where you have (some hypothetical) hardware of this style: .. |a5d2| || |GPO0|--. |GPO1|. | ||| | || .---. || |dg4052a| || | | | ADC|-|XX0| signal X0 |TRGR|--. | X1| signal X1 || | | X2| signal X2 || | | X3| signal X3 || | | | || '--|YY0| trigger Y0 || | Y1| trigger Y1 '' | Y2| trigger Y2 | Y3| trigger Y3 '---' Where signal X0 (when it's selected but the mux) should be sampled as trigger Y0 is rising and
Re: [PATCH v10 1/6] i2c: designware: Cleaning and comment style fixes.
On 2017-06-08 19:36, Luis Oliveira wrote: > complicated to review. The work here won't bring any additional work to > backported fixes because is just style and reordering. I challenge that. If there is an old bug that existed before this patch that is fixed in the future after this patch has been applied, it might very well be hard_er_ to backport that fix to a point before this patch has been applied. So, what do you mean? > @@ -984,12 +984,12 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) > } > > i2c_dw_disable_int(dev); > - r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, > + ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, >dev_name(dev->dev), dev); Two extra spaces needed to align with the opening bracket. Cheers, peda
Re: [PATCH v10 1/6] i2c: designware: Cleaning and comment style fixes.
On 2017-06-09 10:38, Andy Shevchenko wrote: > On Fri, 2017-06-09 at 07:12 +0200, Peter Rosin wrote: >> On 2017-06-08 19:36, Luis Oliveira wrote: >>> @@ -984,12 +984,12 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) >>> } >>> >>> i2c_dw_disable_int(dev); >>> - r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, >>> irq_flags, >>> + ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, >>> irq_flags, >>> dev_name(dev->dev), dev); >> > >> Two extra spaces needed to align with the opening bracket. > > It's a bikeshedding, though it looks like v11 is needed anyway (see > kbuild bot complains), thus it might be addressed as well. > Bikeshedding or not, checkpatch.pl --strict notifies about the issue with: CHECK: Alignment should match open parenthesis Cheers, peda
Re: [PATCH] mux: adg792a: always require I2C support
On 2017-06-09 12:22, Arnd Bergmann wrote: > COMPILE_TEST makes no sense when I2C is disabled, as the driver cannot compile > in that configuration: Ouch, thanks for catching! Reviewed-by: Peter Rosin <p...@axentia.se> Greg, I assume you will you be taking this? Cheers, peda