mbox series

[v3,0/2] Add DSI panel driver for Raydium RM67191

Message ID 1561037428-13855-1-git-send-email-robert.chiras@nxp.com
Headers show
Series Add DSI panel driver for Raydium RM67191 | expand

Message

Robert Chiras June 20, 2019, 1:30 p.m. UTC
This patch-set contains the DRM panel driver and dt-bindings documentation
for the DSI driven panel: Raydium RM67191.

v3:
- Added myself to MAINTAINERS for this driver (sam)
- Removed display-timings property (fabio)
- Fixed dt description (sam)
- Re-arranged calls inside get_modes function (sam)
- Changed ifdefs with _maybe_unused for suspend/resume functions (sam)
- Collected Reviewed-by from Sam

v2:
- Fixed 'reset-gpio' to 'reset-gpios' property naming (fabio)
- Changed the state of the reset gpio to active low and fixed how it is
  handled in driver (fabio)
- Fixed copyright statement (daniel)
- Reordered includes (sam)
- Added defines for panel specific color formats (fabio)
- Removed unnecessary tests in enable and unprepare (sam)
- Removed the unnecessary backlight write in enable (sam)

Robert Chiras (2):
  dt-bindings: display: panel: Add support for Raydium RM67191 panel
  drm/panel: Add support for Raydium RM67191 panel driver

 .../bindings/display/panel/raydium,rm67191.txt     |  39 ++
 MAINTAINERS                                        |   6 +
 drivers/gpu/drm/panel/Kconfig                      |   9 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-raydium-rm67191.c      | 690 +++++++++++++++++++++
 5 files changed, 745 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
 create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c

Comments

Fabio Estevam June 21, 2019, 1:59 p.m. UTC | #1
Hi Robert,

On Thu, Jun 20, 2019 at 10:31 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> +fail:
> +       if (rad->reset)
> +               gpiod_set_value_cansleep(rad->reset, 1);

gpiod_set_value_cansleep() can handle NULL, so no need for the if() check.

> +static const struct display_timing rad_default_timing = {
> +       .pixelclock = { 132000000, 132000000, 132000000 },

Having the same information listed three times does not seem useful.

You could use a drm_display_mode structure with a single entry instead.

> +       videomode_from_timing(&rad_default_timing, &panel->vm);
> +
> +       of_property_read_u32(np, "width-mm", &panel->width_mm);
> +       of_property_read_u32(np, "height-mm", &panel->height_mm);
> +
> +       panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);

Since this is optional it would be better to use
devm_gpiod_get_optional() instead.


> +
> +       if (IS_ERR(panel->reset))
> +               panel->reset = NULL;
> +       else
> +               gpiod_set_value_cansleep(panel->reset, 1);

This is not handling defer probing, so it would be better to do like this:

panel->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(panel->reset))
      return  PTR_ERR(panel->reset);

> +       memset(&bl_props, 0, sizeof(bl_props));
> +       bl_props.type = BACKLIGHT_RAW;
> +       bl_props.brightness = 255;
> +       bl_props.max_brightness = 255;
> +
> +       panel->backlight = devm_backlight_device_register(dev, dev_name(dev),
> +                                                         dev, dsi,
> +                                                         &rad_bl_ops,
> +                                                         &bl_props);

Could you put more parameters into the same line?

Using 4 lines seems excessive.

> +       if (IS_ERR(panel->backlight)) {
> +               ret = PTR_ERR(panel->backlight);
> +               dev_err(dev, "Failed to register backlight (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       drm_panel_init(&panel->panel);
> +       panel->panel.funcs = &rad_panel_funcs;
> +       panel->panel.dev = dev;
> +       dev_set_drvdata(dev, panel);
> +
> +       ret = drm_panel_add(&panel->panel);
> +

Unneeded blank line

> +       if (ret < 0)
> +               return ret;
> +
> +       ret = mipi_dsi_attach(dsi);
> +       if (ret < 0)
> +               drm_panel_remove(&panel->panel);
> +
> +       return ret;

You did not handle the "power" regulator.

> +static int __maybe_unused rad_panel_suspend(struct device *dev)
> +{
> +       struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +       if (!rad->reset)
> +               return 0;
> +
> +       devm_gpiod_put(dev, rad->reset);
> +       rad->reset = NULL;
> +
> +       return 0;
> +}
> +
> +static int __maybe_unused rad_panel_resume(struct device *dev)
> +{
> +       struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +       if (rad->reset)
> +               return 0;
> +
> +       rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);

Why do you call devm_gpiod_get() once again?

I am having a hard time to understand the need for this suspend/resume hooks.

Can't you simply remove them?
Robert Chiras June 24, 2019, 7:44 a.m. UTC | #2
Hi Fabio,

Thanks for your feedback. I will handle them all, but for the pm_ops I
have some comments. See inline.

On Vi, 2019-06-21 at 10:59 -0300, Fabio Estevam wrote:
> Hi Robert,
> 
> On Thu, Jun 20, 2019 at 10:31 AM Robert Chiras <robert.chiras@nxp.com
> > wrote:
> 
> > 
> > +fail:
> > +       if (rad->reset)
> > +               gpiod_set_value_cansleep(rad->reset, 1);
> gpiod_set_value_cansleep() can handle NULL, so no need for the if()
> check.
> 
> > 
> > +static const struct display_timing rad_default_timing = {
> > +       .pixelclock = { 132000000, 132000000, 132000000 },
> Having the same information listed three times does not seem useful.
> 
> You could use a drm_display_mode structure with a single entry
> instead.
> 
> > 
> > +       videomode_from_timing(&rad_default_timing, &panel->vm);
> > +
> > +       of_property_read_u32(np, "width-mm", &panel->width_mm);
> > +       of_property_read_u32(np, "height-mm", &panel->height_mm);
> > +
> > +       panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> Since this is optional it would be better to use
> devm_gpiod_get_optional() instead.
> 
> 
> > 
> > +
> > +       if (IS_ERR(panel->reset))
> > +               panel->reset = NULL;
> > +       else
> > +               gpiod_set_value_cansleep(panel->reset, 1);
> This is not handling defer probing, so it would be better to do like
> this:
> 
> panel->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> if (IS_ERR(panel->reset))
>       return  PTR_ERR(panel->reset);
> 
> > 
> > +       memset(&bl_props, 0, sizeof(bl_props));
> > +       bl_props.type = BACKLIGHT_RAW;
> > +       bl_props.brightness = 255;
> > +       bl_props.max_brightness = 255;
> > +
> > +       panel->backlight = devm_backlight_device_register(dev,
> > dev_name(dev),
> > +                                                         dev, dsi,
> > +                                                         &rad_bl_o
> > ps,
> > +                                                         &bl_props
> > );
> Could you put more parameters into the same line?
> 
> Using 4 lines seems excessive.
> 
> > 
> > +       if (IS_ERR(panel->backlight)) {
> > +               ret = PTR_ERR(panel->backlight);
> > +               dev_err(dev, "Failed to register backlight (%d)\n",
> > ret);
> > +               return ret;
> > +       }
> > +
> > +       drm_panel_init(&panel->panel);
> > +       panel->panel.funcs = &rad_panel_funcs;
> > +       panel->panel.dev = dev;
> > +       dev_set_drvdata(dev, panel);
> > +
> > +       ret = drm_panel_add(&panel->panel);
> > +
> Unneeded blank line
> 
> > 
> > +       if (ret < 0)
> > +               return ret;
> > +
> > +       ret = mipi_dsi_attach(dsi);
> > +       if (ret < 0)
> > +               drm_panel_remove(&panel->panel);
> > +
> > +       return ret;
> You did not handle the "power" regulator.
There is no need for a power regulator.
> 
> > 
> > +static int __maybe_unused rad_panel_suspend(struct device *dev)
> > +{
> > +       struct rad_panel *rad = dev_get_drvdata(dev);
> > +
> > +       if (!rad->reset)
> > +               return 0;
> > +
> > +       devm_gpiod_put(dev, rad->reset);
> > +       rad->reset = NULL;
> > +
> > +       return 0;
> > +}
> > +
> > +static int __maybe_unused rad_panel_resume(struct device *dev)
> > +{
> > +       struct rad_panel *rad = dev_get_drvdata(dev);
> > +
> > +       if (rad->reset)
> > +               return 0;
> > +
> > +       rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> Why do you call devm_gpiod_get() once again?
> 
> I am having a hard time to understand the need for this
> suspend/resume hooks.
> 
> Can't you simply remove them?
The reset gpio pin is shared between the DSI display controller and
touch controller, both found on the same panel. Since, the touch driver
also acceses this gpio pin, in some cases the display can be put to
sleep, but the touch is still active. This is why, during suspend I
release the gpio resource, and I have to take it back in resume.
Without this release/acquire mechanism during suspend/resume, stress-
tests showed some failures: the resume freezes completly.
Fabio Estevam June 24, 2019, 4:12 p.m. UTC | #3
Hi Robert,

On Mon, Jun 24, 2019 at 4:44 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> > You did not handle the "power" regulator.
> There is no need for a power regulator.

I am sure the panel requires power to be applied so that it can work :-)

> > Can't you simply remove them?
> The reset gpio pin is shared between the DSI display controller and

Looking at the imx8mq-evk schematics it is not clear for me what pin
in shared between the OLED display and touch screen.

Could you please clarify which pin you are referring to?

> touch controller, both found on the same panel. Since, the touch driver
> also acceses this gpio pin, in some cases the display can be put to
> sleep, but the touch is still active. This is why, during suspend I
> release the gpio resource, and I have to take it back in resume.
> Without this release/acquire mechanism during suspend/resume, stress-
> tests showed some failures: the resume freezes completly.

Looking at the imx8mq-evk dts from the vendor tree I see that the
touchscreen is not supported in mainline yet.

Maybe there is a better way to solve this, so what if you don't
introduce the suspend/resume hooks for now and then we revisit this
after the touchscreen driver is added?
Robert Chiras June 25, 2019, 6:35 a.m. UTC | #4
On Lu, 2019-06-24 at 13:12 -0300, Fabio Estevam wrote:
> Caution: EXT Email
> 
> Hi Robert,
> 
> On Mon, Jun 24, 2019 at 4:44 AM Robert Chiras <robert.chiras@nxp.com>
> wrote:
> 
> > 
> > > 
> > > You did not handle the "power" regulator.
> > There is no need for a power regulator.
> I am sure the panel requires power to be applied so that it can work
> :-)
Yes, of course there are power lines to the panel. I will add them to
the next revision.
> 
> > 
> > > 
> > > Can't you simply remove them?
> > The reset gpio pin is shared between the DSI display controller and
> Looking at the imx8mq-evk schematics it is not clear for me what pin
> in shared between the OLED display and touch screen.
> 
> Could you please clarify which pin you are referring to?
It's about the DSI_EN pin on the DSI LCD IF physical port J1501. This
goes into RST_B_1V8 on the panel port which is used for both display
and touch resets.
> 
> > 
> > touch controller, both found on the same panel. Since, the touch
> > driver
> > also acceses this gpio pin, in some cases the display can be put to
> > sleep, but the touch is still active. This is why, during suspend I
> > release the gpio resource, and I have to take it back in resume.
> > Without this release/acquire mechanism during suspend/resume,
> > stress-
> > tests showed some failures: the resume freezes completly.
> Looking at the imx8mq-evk dts from the vendor tree I see that the
> touchscreen is not supported in mainline yet.
> 
> Maybe there is a better way to solve this, so what if you don't
> introduce the suspend/resume hooks for now and then we revisit this
> after the touchscreen driver is added?
You are right. We can do this too. I will remove it for now.