Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-14 Thread Maxime Ripard
On Thu, Dec 14, 2017 at 11:30:21AM +0800, Chen-Yu Tsai wrote:
> >> > +   /* Map output pins to channel 0 */
> >> > +   regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
> >> > +  SUN4I_TCON_GCTL_IOMAP_MASK,
> >> > +  SUN4I_TCON_GCTL_IOMAP_TCON0);
> >> > +
> >> > +   /* Enable the output on the pins */
> >> > +   regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0xe000);
> >>
> >> Is this still needed? You are no longer using the TCON LCD pins
> >> with LVDS.
> >
> > We do. It's a separate function of the pins, but it's the same pins.
> 
> OK. I assume you've tried it without setting it and it failed?
> I just assume that these refer to the TCON LCD output, whereas
> LVDS looks like a separate module and function, and shouldn't
> need it.

Argh, I forgot to test that. I'll test it for real this time and will
update that part with a comment if that's needed.

Sorry :/

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


signature.asc
Description: PGP signature


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-14 Thread Maxime Ripard
On Thu, Dec 14, 2017 at 11:30:21AM +0800, Chen-Yu Tsai wrote:
> >> > +   /* Map output pins to channel 0 */
> >> > +   regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
> >> > +  SUN4I_TCON_GCTL_IOMAP_MASK,
> >> > +  SUN4I_TCON_GCTL_IOMAP_TCON0);
> >> > +
> >> > +   /* Enable the output on the pins */
> >> > +   regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0xe000);
> >>
> >> Is this still needed? You are no longer using the TCON LCD pins
> >> with LVDS.
> >
> > We do. It's a separate function of the pins, but it's the same pins.
> 
> OK. I assume you've tried it without setting it and it failed?
> I just assume that these refer to the TCON LCD output, whereas
> LVDS looks like a separate module and function, and shouldn't
> need it.

Argh, I forgot to test that. I'll test it for real this time and will
update that part with a comment if that's needed.

Sorry :/

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


signature.asc
Description: PGP signature


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-13 Thread Chen-Yu Tsai
On Thu, Dec 7, 2017 at 8:25 PM, Maxime Ripard
 wrote:
> Hi,
>
> On Thu, Dec 07, 2017 at 02:05:47PM +0800, Chen-Yu Tsai wrote:
>> > +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
>> > +  const struct drm_encoder *encoder,
>> > +  bool enabled)
>> > +{
>> > +   if (enabled) {
>> > +   u8 val;
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
>> > +  SUN4I_TCON0_LVDS_IF_EN,
>> > +  SUN4I_TCON0_LVDS_IF_EN);
>> > +
>> > +   regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +SUN4I_TCON0_LVDS_ANA0_C(2) |
>> > +SUN4I_TCON0_LVDS_ANA0_V(3) |
>> > +SUN4I_TCON0_LVDS_ANA0_PD(2) |
>> > +SUN4I_TCON0_LVDS_ANA0_EN_LDO);
>> > +   udelay(2);
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB);
>> > +   udelay(2);
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC);
>> > +
>> > +   if (sun4i_tcon_get_pixel_depth(encoder) == 18)
>> > +   val = 7;
>> > +   else
>> > +   val = 0xf;
>> > +
>> > +   regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
>> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(val));
>>
>> I suggest changing the prefix of the macros of the analog bits to
>> SUN6I_TCON0_*. The register definitions and sequence do not apply
>> to the A10/A20. Furthermore you should add a comment saying this
>> doesn't apply to the A10/A20. In the future we might want to move
>> this part into a separate function, referenced by a function pointer
>> from the quirks structure.
>
> I'll change the bit field names and add a comment like you suggested.
>
>> > +   } else {
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
>> > +  SUN4I_TCON0_LVDS_IF_EN, 0);
>> > +   }
>> > +}
>> > +
>> >  void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>> >const struct drm_encoder *encoder,
>> >bool enabled)
>> >  {
>> > +   bool is_lvds = false;
>> > int channel;
>> >
>> > switch (encoder->encoder_type) {
>> > +   case DRM_MODE_ENCODER_LVDS:
>> > +   is_lvds = true;
>> > +   /* Fallthrough */
>> > case DRM_MODE_ENCODER_NONE:
>> > channel = 0;
>> > break;
>> > @@ -84,10 +171,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>> > return;
>> > }
>> >
>> > +   if (is_lvds && !enabled)
>> > +   sun4i_tcon_lvds_set_status(tcon, encoder, false);
>> > +
>> > regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
>> >SUN4I_TCON_GCTL_TCON_ENABLE,
>> >enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
>> >
>> > +   if (is_lvds && enabled)
>> > +   sun4i_tcon_lvds_set_status(tcon, encoder, true);
>> > +
>> > sun4i_tcon_channel_set_status(tcon, channel, enabled);
>> >  }
>> >
>> > @@ -170,6 +263,78 @@ static void sun4i_tcon0_mode_set_common(struct 
>> > sun4i_tcon *tcon,
>> >  SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
>> >  }
>> >
>> > +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
>> > + const struct drm_encoder *encoder,
>> > + const struct drm_display_mode *mode)
>> > +{
>> > +   unsigned int bp;
>> > +   u8 clk_delay;
>> > +   u32 reg, val = 0;
>> > +
>> > +   tcon->dclk_min_div = 7;
>> > +   tcon->dclk_max_div = 7;
>> > +   sun4i_tcon0_mode_set_common(tcon, mode);
>> > +
>> > +   /* Adjust clock delay */
>> > +   clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
>> > +  SUN4I_TCON0_CTL_CLK_DELAY_MASK,
>> > +  SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
>> > +
>> > +   /*
>> > +* This is called a backporch in the register documentation,
>> > +* but it really is the back porch + hsync
>> > +*/
>> > +   bp = mode->crtc_htotal - mode->crtc_hsync_start;
>> > +   DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
>> > +   

Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-13 Thread Chen-Yu Tsai
On Thu, Dec 7, 2017 at 8:25 PM, Maxime Ripard
 wrote:
> Hi,
>
> On Thu, Dec 07, 2017 at 02:05:47PM +0800, Chen-Yu Tsai wrote:
>> > +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
>> > +  const struct drm_encoder *encoder,
>> > +  bool enabled)
>> > +{
>> > +   if (enabled) {
>> > +   u8 val;
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
>> > +  SUN4I_TCON0_LVDS_IF_EN,
>> > +  SUN4I_TCON0_LVDS_IF_EN);
>> > +
>> > +   regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +SUN4I_TCON0_LVDS_ANA0_C(2) |
>> > +SUN4I_TCON0_LVDS_ANA0_V(3) |
>> > +SUN4I_TCON0_LVDS_ANA0_PD(2) |
>> > +SUN4I_TCON0_LVDS_ANA0_EN_LDO);
>> > +   udelay(2);
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB);
>> > +   udelay(2);
>> > +
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC,
>> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC);
>> > +
>> > +   if (sun4i_tcon_get_pixel_depth(encoder) == 18)
>> > +   val = 7;
>> > +   else
>> > +   val = 0xf;
>> > +
>> > +   regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
>> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
>> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(val));
>>
>> I suggest changing the prefix of the macros of the analog bits to
>> SUN6I_TCON0_*. The register definitions and sequence do not apply
>> to the A10/A20. Furthermore you should add a comment saying this
>> doesn't apply to the A10/A20. In the future we might want to move
>> this part into a separate function, referenced by a function pointer
>> from the quirks structure.
>
> I'll change the bit field names and add a comment like you suggested.
>
>> > +   } else {
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
>> > +  SUN4I_TCON0_LVDS_IF_EN, 0);
>> > +   }
>> > +}
>> > +
>> >  void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>> >const struct drm_encoder *encoder,
>> >bool enabled)
>> >  {
>> > +   bool is_lvds = false;
>> > int channel;
>> >
>> > switch (encoder->encoder_type) {
>> > +   case DRM_MODE_ENCODER_LVDS:
>> > +   is_lvds = true;
>> > +   /* Fallthrough */
>> > case DRM_MODE_ENCODER_NONE:
>> > channel = 0;
>> > break;
>> > @@ -84,10 +171,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>> > return;
>> > }
>> >
>> > +   if (is_lvds && !enabled)
>> > +   sun4i_tcon_lvds_set_status(tcon, encoder, false);
>> > +
>> > regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
>> >SUN4I_TCON_GCTL_TCON_ENABLE,
>> >enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
>> >
>> > +   if (is_lvds && enabled)
>> > +   sun4i_tcon_lvds_set_status(tcon, encoder, true);
>> > +
>> > sun4i_tcon_channel_set_status(tcon, channel, enabled);
>> >  }
>> >
>> > @@ -170,6 +263,78 @@ static void sun4i_tcon0_mode_set_common(struct 
>> > sun4i_tcon *tcon,
>> >  SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
>> >  }
>> >
>> > +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
>> > + const struct drm_encoder *encoder,
>> > + const struct drm_display_mode *mode)
>> > +{
>> > +   unsigned int bp;
>> > +   u8 clk_delay;
>> > +   u32 reg, val = 0;
>> > +
>> > +   tcon->dclk_min_div = 7;
>> > +   tcon->dclk_max_div = 7;
>> > +   sun4i_tcon0_mode_set_common(tcon, mode);
>> > +
>> > +   /* Adjust clock delay */
>> > +   clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
>> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
>> > +  SUN4I_TCON0_CTL_CLK_DELAY_MASK,
>> > +  SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
>> > +
>> > +   /*
>> > +* This is called a backporch in the register documentation,
>> > +* but it really is the back porch + hsync
>> > +*/
>> > +   bp = mode->crtc_htotal - mode->crtc_hsync_start;
>> > +   DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
>> > +mode->crtc_htotal, bp);
>> > 

Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Maxime Ripard
Hi,

On Thu, Dec 07, 2017 at 02:05:47PM +0800, Chen-Yu Tsai wrote:
> > +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
> > +  const struct drm_encoder *encoder,
> > +  bool enabled)
> > +{
> > +   if (enabled) {
> > +   u8 val;
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> > +  SUN4I_TCON0_LVDS_IF_EN,
> > +  SUN4I_TCON0_LVDS_IF_EN);
> > +
> > +   regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +SUN4I_TCON0_LVDS_ANA0_C(2) |
> > +SUN4I_TCON0_LVDS_ANA0_V(3) |
> > +SUN4I_TCON0_LVDS_ANA0_PD(2) |
> > +SUN4I_TCON0_LVDS_ANA0_EN_LDO);
> > +   udelay(2);
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB);
> > +   udelay(2);
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC);
> > +
> > +   if (sun4i_tcon_get_pixel_depth(encoder) == 18)
> > +   val = 7;
> > +   else
> > +   val = 0xf;
> > +
> > +   regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(val));
> 
> I suggest changing the prefix of the macros of the analog bits to
> SUN6I_TCON0_*. The register definitions and sequence do not apply
> to the A10/A20. Furthermore you should add a comment saying this
> doesn't apply to the A10/A20. In the future we might want to move
> this part into a separate function, referenced by a function pointer
> from the quirks structure.

I'll change the bit field names and add a comment like you suggested.

> > +   } else {
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> > +  SUN4I_TCON0_LVDS_IF_EN, 0);
> > +   }
> > +}
> > +
> >  void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
> >const struct drm_encoder *encoder,
> >bool enabled)
> >  {
> > +   bool is_lvds = false;
> > int channel;
> >
> > switch (encoder->encoder_type) {
> > +   case DRM_MODE_ENCODER_LVDS:
> > +   is_lvds = true;
> > +   /* Fallthrough */
> > case DRM_MODE_ENCODER_NONE:
> > channel = 0;
> > break;
> > @@ -84,10 +171,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
> > return;
> > }
> >
> > +   if (is_lvds && !enabled)
> > +   sun4i_tcon_lvds_set_status(tcon, encoder, false);
> > +
> > regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
> >SUN4I_TCON_GCTL_TCON_ENABLE,
> >enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
> >
> > +   if (is_lvds && enabled)
> > +   sun4i_tcon_lvds_set_status(tcon, encoder, true);
> > +
> > sun4i_tcon_channel_set_status(tcon, channel, enabled);
> >  }
> >
> > @@ -170,6 +263,78 @@ static void sun4i_tcon0_mode_set_common(struct 
> > sun4i_tcon *tcon,
> >  SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
> >  }
> >
> > +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
> > + const struct drm_encoder *encoder,
> > + const struct drm_display_mode *mode)
> > +{
> > +   unsigned int bp;
> > +   u8 clk_delay;
> > +   u32 reg, val = 0;
> > +
> > +   tcon->dclk_min_div = 7;
> > +   tcon->dclk_max_div = 7;
> > +   sun4i_tcon0_mode_set_common(tcon, mode);
> > +
> > +   /* Adjust clock delay */
> > +   clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
> > +  SUN4I_TCON0_CTL_CLK_DELAY_MASK,
> > +  SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
> > +
> > +   /*
> > +* This is called a backporch in the register documentation,
> > +* but it really is the back porch + hsync
> > +*/
> > +   bp = mode->crtc_htotal - mode->crtc_hsync_start;
> > +   DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
> > +mode->crtc_htotal, bp);
> > +
> > +   /* Set horizontal display timings */
> > +   regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
> > +

Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Maxime Ripard
Hi,

On Thu, Dec 07, 2017 at 02:05:47PM +0800, Chen-Yu Tsai wrote:
> > +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
> > +  const struct drm_encoder *encoder,
> > +  bool enabled)
> > +{
> > +   if (enabled) {
> > +   u8 val;
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> > +  SUN4I_TCON0_LVDS_IF_EN,
> > +  SUN4I_TCON0_LVDS_IF_EN);
> > +
> > +   regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +SUN4I_TCON0_LVDS_ANA0_C(2) |
> > +SUN4I_TCON0_LVDS_ANA0_V(3) |
> > +SUN4I_TCON0_LVDS_ANA0_PD(2) |
> > +SUN4I_TCON0_LVDS_ANA0_EN_LDO);
> > +   udelay(2);
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_MB);
> > +   udelay(2);
> > +
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC,
> > +  SUN4I_TCON0_LVDS_ANA0_EN_DRVC);
> > +
> > +   if (sun4i_tcon_get_pixel_depth(encoder) == 18)
> > +   val = 7;
> > +   else
> > +   val = 0xf;
> > +
> > +   regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
> > + SUN4I_TCON0_LVDS_ANA0_EN_DRVD(val));
> 
> I suggest changing the prefix of the macros of the analog bits to
> SUN6I_TCON0_*. The register definitions and sequence do not apply
> to the A10/A20. Furthermore you should add a comment saying this
> doesn't apply to the A10/A20. In the future we might want to move
> this part into a separate function, referenced by a function pointer
> from the quirks structure.

I'll change the bit field names and add a comment like you suggested.

> > +   } else {
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> > +  SUN4I_TCON0_LVDS_IF_EN, 0);
> > +   }
> > +}
> > +
> >  void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
> >const struct drm_encoder *encoder,
> >bool enabled)
> >  {
> > +   bool is_lvds = false;
> > int channel;
> >
> > switch (encoder->encoder_type) {
> > +   case DRM_MODE_ENCODER_LVDS:
> > +   is_lvds = true;
> > +   /* Fallthrough */
> > case DRM_MODE_ENCODER_NONE:
> > channel = 0;
> > break;
> > @@ -84,10 +171,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
> > return;
> > }
> >
> > +   if (is_lvds && !enabled)
> > +   sun4i_tcon_lvds_set_status(tcon, encoder, false);
> > +
> > regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
> >SUN4I_TCON_GCTL_TCON_ENABLE,
> >enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
> >
> > +   if (is_lvds && enabled)
> > +   sun4i_tcon_lvds_set_status(tcon, encoder, true);
> > +
> > sun4i_tcon_channel_set_status(tcon, channel, enabled);
> >  }
> >
> > @@ -170,6 +263,78 @@ static void sun4i_tcon0_mode_set_common(struct 
> > sun4i_tcon *tcon,
> >  SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
> >  }
> >
> > +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
> > + const struct drm_encoder *encoder,
> > + const struct drm_display_mode *mode)
> > +{
> > +   unsigned int bp;
> > +   u8 clk_delay;
> > +   u32 reg, val = 0;
> > +
> > +   tcon->dclk_min_div = 7;
> > +   tcon->dclk_max_div = 7;
> > +   sun4i_tcon0_mode_set_common(tcon, mode);
> > +
> > +   /* Adjust clock delay */
> > +   clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
> > +   regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
> > +  SUN4I_TCON0_CTL_CLK_DELAY_MASK,
> > +  SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
> > +
> > +   /*
> > +* This is called a backporch in the register documentation,
> > +* but it really is the back porch + hsync
> > +*/
> > +   bp = mode->crtc_htotal - mode->crtc_hsync_start;
> > +   DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
> > +mode->crtc_htotal, bp);
> > +
> > +   /* Set horizontal display timings */
> > +   regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
> > +

Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Maxime Ripard
Hi,

On Thu, Dec 07, 2017 at 11:14:27AM +0100, Philippe Ombredanne wrote:
> On Tue, Dec 5, 2017 at 4:10 PM, Maxime Ripard
>  wrote:
> > The TCON supports the LVDS interface to output to a panel or a bridge.
> > Let's add support for it.
> >
> > Signed-off-by: Maxime Ripard 
> []
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> > @@ -0,0 +1,183 @@
> > +/*
> > + * Copyright (C) 2015 NextThing Co
> > + * Copyright (C) 2015-2017 Free Electrons
> > + *
> > + * Maxime Ripard 
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation; either version 2 of
> > + * the License, or (at your option) any later version.
> > + */
> 
> Would you consider using the new SPDX ids instead of this fine legalese?
> e.g. this as the top line:
> 
> // SPDX-License-Identifier: GPL-2.0+

I did, and then forgot about it.

This will be in my next iteration, thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


signature.asc
Description: PGP signature


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Maxime Ripard
Hi,

On Thu, Dec 07, 2017 at 11:14:27AM +0100, Philippe Ombredanne wrote:
> On Tue, Dec 5, 2017 at 4:10 PM, Maxime Ripard
>  wrote:
> > The TCON supports the LVDS interface to output to a panel or a bridge.
> > Let's add support for it.
> >
> > Signed-off-by: Maxime Ripard 
> []
> > --- /dev/null
> > +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> > @@ -0,0 +1,183 @@
> > +/*
> > + * Copyright (C) 2015 NextThing Co
> > + * Copyright (C) 2015-2017 Free Electrons
> > + *
> > + * Maxime Ripard 
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation; either version 2 of
> > + * the License, or (at your option) any later version.
> > + */
> 
> Would you consider using the new SPDX ids instead of this fine legalese?
> e.g. this as the top line:
> 
> // SPDX-License-Identifier: GPL-2.0+

I did, and then forgot about it.

This will be in my next iteration, thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


signature.asc
Description: PGP signature


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Philippe Ombredanne
On Tue, Dec 5, 2017 at 4:10 PM, Maxime Ripard
 wrote:
> The TCON supports the LVDS interface to output to a panel or a bridge.
> Let's add support for it.
>
> Signed-off-by: Maxime Ripard 
[]
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -0,0 +1,183 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard 
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */

Maxime,

Would you consider using the new SPDX ids instead of this fine legalese?
e.g. this as the top line:

// SPDX-License-Identifier: GPL-2.0+

-- 
Cordially
Philippe Ombredanne


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-07 Thread Philippe Ombredanne
On Tue, Dec 5, 2017 at 4:10 PM, Maxime Ripard
 wrote:
> The TCON supports the LVDS interface to output to a panel or a bridge.
> Let's add support for it.
>
> Signed-off-by: Maxime Ripard 
[]
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -0,0 +1,183 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard 
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */

Maxime,

Would you consider using the new SPDX ids instead of this fine legalese?
e.g. this as the top line:

// SPDX-License-Identifier: GPL-2.0+

-- 
Cordially
Philippe Ombredanne


Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-06 Thread Chen-Yu Tsai
On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
 wrote:
> The TCON supports the LVDS interface to output to a panel or a bridge.
> Let's add support for it.
>
> Signed-off-by: Maxime Ripard 
> ---
>  drivers/gpu/drm/sun4i/Makefile |   1 +-
>  drivers/gpu/drm/sun4i/sun4i_lvds.c | 183 +++-
>  drivers/gpu/drm/sun4i/sun4i_lvds.h |  18 ++-
>  drivers/gpu/drm/sun4i/sun4i_tcon.c | 238 +-
>  drivers/gpu/drm/sun4i/sun4i_tcon.h |  29 -
>  5 files changed, 467 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.h
>
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 82a6ac57fbe3..2b37a6abbb1d 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -15,6 +15,7 @@ sun8i-mixer-y += sun8i_mixer.o 
> sun8i_ui_layer.o \
>
>  sun4i-tcon-y   += sun4i_crtc.o
>  sun4i-tcon-y   += sun4i_dotclock.o
> +sun4i-tcon-y   += sun4i_lvds.o
>  sun4i-tcon-y   += sun4i_tcon.o
>  sun4i-tcon-y   += sun4i_rgb.o
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c 
> b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> new file mode 100644
> index ..635a3f505ecb
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -0,0 +1,183 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard 
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "sun4i_crtc.h"
> +#include "sun4i_tcon.h"
> +#include "sun4i_lvds.h"
> +
> +struct sun4i_lvds {
> +   struct drm_connectorconnector;
> +   struct drm_encoder  encoder;
> +
> +   struct sun4i_tcon   *tcon;
> +};
> +
> +static inline struct sun4i_lvds *
> +drm_connector_to_sun4i_lvds(struct drm_connector *connector)
> +{
> +   return container_of(connector, struct sun4i_lvds,
> +   connector);
> +}
> +
> +static inline struct sun4i_lvds *
> +drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
> +{
> +   return container_of(encoder, struct sun4i_lvds,
> +   encoder);
> +}
> +
> +static int sun4i_lvds_get_modes(struct drm_connector *connector)
> +{
> +   struct sun4i_lvds *lvds =
> +   drm_connector_to_sun4i_lvds(connector);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   return drm_panel_get_modes(tcon->panel);
> +}
> +
> +static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
> +   .get_modes  = sun4i_lvds_get_modes,
> +};
> +
> +static void
> +sun4i_lvds_connector_destroy(struct drm_connector *connector)
> +{
> +   struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   drm_panel_detach(tcon->panel);
> +   drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
> +   .fill_modes = drm_helper_probe_single_connector_modes,
> +   .destroy= sun4i_lvds_connector_destroy,
> +   .reset  = drm_atomic_helper_connector_reset,
> +   .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +   .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
> +{
> +   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   DRM_DEBUG_DRIVER("Enabling LVDS output\n");
> +
> +   if (!IS_ERR(tcon->panel)) {
> +   drm_panel_prepare(tcon->panel);
> +   drm_panel_enable(tcon->panel);
> +   }
> +}
> +
> +static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
> +{
> +   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   DRM_DEBUG_DRIVER("Disabling LVDS output\n");
> +
> +   if (!IS_ERR(tcon->panel)) {
> +   drm_panel_disable(tcon->panel);
> +   drm_panel_unprepare(tcon->panel);
> +   }
> +}
> +
> +static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
> +   .disable= sun4i_lvds_encoder_disable,
> +   .enable = sun4i_lvds_encoder_enable,
> +};
> +
> +static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
> +   .destroy= 

Re: [PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-06 Thread Chen-Yu Tsai
On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
 wrote:
> The TCON supports the LVDS interface to output to a panel or a bridge.
> Let's add support for it.
>
> Signed-off-by: Maxime Ripard 
> ---
>  drivers/gpu/drm/sun4i/Makefile |   1 +-
>  drivers/gpu/drm/sun4i/sun4i_lvds.c | 183 +++-
>  drivers/gpu/drm/sun4i/sun4i_lvds.h |  18 ++-
>  drivers/gpu/drm/sun4i/sun4i_tcon.c | 238 +-
>  drivers/gpu/drm/sun4i/sun4i_tcon.h |  29 -
>  5 files changed, 467 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.h
>
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 82a6ac57fbe3..2b37a6abbb1d 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -15,6 +15,7 @@ sun8i-mixer-y += sun8i_mixer.o 
> sun8i_ui_layer.o \
>
>  sun4i-tcon-y   += sun4i_crtc.o
>  sun4i-tcon-y   += sun4i_dotclock.o
> +sun4i-tcon-y   += sun4i_lvds.o
>  sun4i-tcon-y   += sun4i_tcon.o
>  sun4i-tcon-y   += sun4i_rgb.o
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c 
> b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> new file mode 100644
> index ..635a3f505ecb
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -0,0 +1,183 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard 
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "sun4i_crtc.h"
> +#include "sun4i_tcon.h"
> +#include "sun4i_lvds.h"
> +
> +struct sun4i_lvds {
> +   struct drm_connectorconnector;
> +   struct drm_encoder  encoder;
> +
> +   struct sun4i_tcon   *tcon;
> +};
> +
> +static inline struct sun4i_lvds *
> +drm_connector_to_sun4i_lvds(struct drm_connector *connector)
> +{
> +   return container_of(connector, struct sun4i_lvds,
> +   connector);
> +}
> +
> +static inline struct sun4i_lvds *
> +drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
> +{
> +   return container_of(encoder, struct sun4i_lvds,
> +   encoder);
> +}
> +
> +static int sun4i_lvds_get_modes(struct drm_connector *connector)
> +{
> +   struct sun4i_lvds *lvds =
> +   drm_connector_to_sun4i_lvds(connector);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   return drm_panel_get_modes(tcon->panel);
> +}
> +
> +static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
> +   .get_modes  = sun4i_lvds_get_modes,
> +};
> +
> +static void
> +sun4i_lvds_connector_destroy(struct drm_connector *connector)
> +{
> +   struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   drm_panel_detach(tcon->panel);
> +   drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
> +   .fill_modes = drm_helper_probe_single_connector_modes,
> +   .destroy= sun4i_lvds_connector_destroy,
> +   .reset  = drm_atomic_helper_connector_reset,
> +   .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +   .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
> +{
> +   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   DRM_DEBUG_DRIVER("Enabling LVDS output\n");
> +
> +   if (!IS_ERR(tcon->panel)) {
> +   drm_panel_prepare(tcon->panel);
> +   drm_panel_enable(tcon->panel);
> +   }
> +}
> +
> +static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
> +{
> +   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +   struct sun4i_tcon *tcon = lvds->tcon;
> +
> +   DRM_DEBUG_DRIVER("Disabling LVDS output\n");
> +
> +   if (!IS_ERR(tcon->panel)) {
> +   drm_panel_disable(tcon->panel);
> +   drm_panel_unprepare(tcon->panel);
> +   }
> +}
> +
> +static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
> +   .disable= sun4i_lvds_encoder_disable,
> +   .enable = sun4i_lvds_encoder_enable,
> +};
> +
> +static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
> +   .destroy= drm_encoder_cleanup,
> +};
> +
> +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
> +{
> 

[PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-05 Thread Maxime Ripard
The TCON supports the LVDS interface to output to a panel or a bridge.
Let's add support for it.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/sun4i/Makefile |   1 +-
 drivers/gpu/drm/sun4i/sun4i_lvds.c | 183 +++-
 drivers/gpu/drm/sun4i/sun4i_lvds.h |  18 ++-
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 238 +-
 drivers/gpu/drm/sun4i/sun4i_tcon.h |  29 -
 5 files changed, 467 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 82a6ac57fbe3..2b37a6abbb1d 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -15,6 +15,7 @@ sun8i-mixer-y += sun8i_mixer.o 
sun8i_ui_layer.o \
 
 sun4i-tcon-y   += sun4i_crtc.o
 sun4i-tcon-y   += sun4i_dotclock.o
+sun4i-tcon-y   += sun4i_lvds.o
 sun4i-tcon-y   += sun4i_tcon.o
 sun4i-tcon-y   += sun4i_rgb.o
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c 
b/drivers/gpu/drm/sun4i/sun4i_lvds.c
new file mode 100644
index ..635a3f505ecb
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 NextThing Co
+ * Copyright (C) 2015-2017 Free Electrons
+ *
+ * Maxime Ripard 
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "sun4i_crtc.h"
+#include "sun4i_tcon.h"
+#include "sun4i_lvds.h"
+
+struct sun4i_lvds {
+   struct drm_connectorconnector;
+   struct drm_encoder  encoder;
+
+   struct sun4i_tcon   *tcon;
+};
+
+static inline struct sun4i_lvds *
+drm_connector_to_sun4i_lvds(struct drm_connector *connector)
+{
+   return container_of(connector, struct sun4i_lvds,
+   connector);
+}
+
+static inline struct sun4i_lvds *
+drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
+{
+   return container_of(encoder, struct sun4i_lvds,
+   encoder);
+}
+
+static int sun4i_lvds_get_modes(struct drm_connector *connector)
+{
+   struct sun4i_lvds *lvds =
+   drm_connector_to_sun4i_lvds(connector);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   return drm_panel_get_modes(tcon->panel);
+}
+
+static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
+   .get_modes  = sun4i_lvds_get_modes,
+};
+
+static void
+sun4i_lvds_connector_destroy(struct drm_connector *connector)
+{
+   struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   drm_panel_detach(tcon->panel);
+   drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
+   .fill_modes = drm_helper_probe_single_connector_modes,
+   .destroy= sun4i_lvds_connector_destroy,
+   .reset  = drm_atomic_helper_connector_reset,
+   .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+   .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
+};
+
+static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
+{
+   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   DRM_DEBUG_DRIVER("Enabling LVDS output\n");
+
+   if (!IS_ERR(tcon->panel)) {
+   drm_panel_prepare(tcon->panel);
+   drm_panel_enable(tcon->panel);
+   }
+}
+
+static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   DRM_DEBUG_DRIVER("Disabling LVDS output\n");
+
+   if (!IS_ERR(tcon->panel)) {
+   drm_panel_disable(tcon->panel);
+   drm_panel_unprepare(tcon->panel);
+   }
+}
+
+static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
+   .disable= sun4i_lvds_encoder_disable,
+   .enable = sun4i_lvds_encoder_enable,
+};
+
+static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
+   .destroy= drm_encoder_cleanup,
+};
+
+int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
+{
+   struct drm_encoder *encoder;
+   struct drm_bridge *bridge;
+   struct sun4i_lvds *lvds;
+   int ret;
+
+   lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
+   if (!lvds)
+   return -ENOMEM;
+   lvds->tcon = tcon;
+   

[PATCH v3 08/15] drm/sun4i: Add LVDS support

2017-12-05 Thread Maxime Ripard
The TCON supports the LVDS interface to output to a panel or a bridge.
Let's add support for it.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/sun4i/Makefile |   1 +-
 drivers/gpu/drm/sun4i/sun4i_lvds.c | 183 +++-
 drivers/gpu/drm/sun4i/sun4i_lvds.h |  18 ++-
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 238 +-
 drivers/gpu/drm/sun4i/sun4i_tcon.h |  29 -
 5 files changed, 467 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 82a6ac57fbe3..2b37a6abbb1d 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -15,6 +15,7 @@ sun8i-mixer-y += sun8i_mixer.o 
sun8i_ui_layer.o \
 
 sun4i-tcon-y   += sun4i_crtc.o
 sun4i-tcon-y   += sun4i_dotclock.o
+sun4i-tcon-y   += sun4i_lvds.o
 sun4i-tcon-y   += sun4i_tcon.o
 sun4i-tcon-y   += sun4i_rgb.o
 
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c 
b/drivers/gpu/drm/sun4i/sun4i_lvds.c
new file mode 100644
index ..635a3f505ecb
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 NextThing Co
+ * Copyright (C) 2015-2017 Free Electrons
+ *
+ * Maxime Ripard 
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "sun4i_crtc.h"
+#include "sun4i_tcon.h"
+#include "sun4i_lvds.h"
+
+struct sun4i_lvds {
+   struct drm_connectorconnector;
+   struct drm_encoder  encoder;
+
+   struct sun4i_tcon   *tcon;
+};
+
+static inline struct sun4i_lvds *
+drm_connector_to_sun4i_lvds(struct drm_connector *connector)
+{
+   return container_of(connector, struct sun4i_lvds,
+   connector);
+}
+
+static inline struct sun4i_lvds *
+drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
+{
+   return container_of(encoder, struct sun4i_lvds,
+   encoder);
+}
+
+static int sun4i_lvds_get_modes(struct drm_connector *connector)
+{
+   struct sun4i_lvds *lvds =
+   drm_connector_to_sun4i_lvds(connector);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   return drm_panel_get_modes(tcon->panel);
+}
+
+static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
+   .get_modes  = sun4i_lvds_get_modes,
+};
+
+static void
+sun4i_lvds_connector_destroy(struct drm_connector *connector)
+{
+   struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   drm_panel_detach(tcon->panel);
+   drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
+   .fill_modes = drm_helper_probe_single_connector_modes,
+   .destroy= sun4i_lvds_connector_destroy,
+   .reset  = drm_atomic_helper_connector_reset,
+   .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+   .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
+};
+
+static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
+{
+   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   DRM_DEBUG_DRIVER("Enabling LVDS output\n");
+
+   if (!IS_ERR(tcon->panel)) {
+   drm_panel_prepare(tcon->panel);
+   drm_panel_enable(tcon->panel);
+   }
+}
+
+static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+   struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
+   struct sun4i_tcon *tcon = lvds->tcon;
+
+   DRM_DEBUG_DRIVER("Disabling LVDS output\n");
+
+   if (!IS_ERR(tcon->panel)) {
+   drm_panel_disable(tcon->panel);
+   drm_panel_unprepare(tcon->panel);
+   }
+}
+
+static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
+   .disable= sun4i_lvds_encoder_disable,
+   .enable = sun4i_lvds_encoder_enable,
+};
+
+static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
+   .destroy= drm_encoder_cleanup,
+};
+
+int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
+{
+   struct drm_encoder *encoder;
+   struct drm_bridge *bridge;
+   struct sun4i_lvds *lvds;
+   int ret;
+
+   lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
+   if (!lvds)
+   return -ENOMEM;
+   lvds->tcon = tcon;
+   encoder = >encoder;
+
+   ret =