diff mbox

[v6,5/9] drm/hisilicon/hibmc: Add crtc for DE

Message ID 1477639682-22520-6-git-send-email-zourongrong@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rongrong Zou Oct. 28, 2016, 7:27 a.m. UTC
Add crtc funcs and helper funcs for DE.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 318 ++++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |   6 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   2 +
 3 files changed, 326 insertions(+)

Comments

Sean Paul Nov. 10, 2016, 10:14 p.m. UTC | #1
On Fri, Oct 28, 2016 at 3:27 AM, Rongrong Zou <zourongrong@gmail.com> wrote:
> Add crtc funcs and helper funcs for DE.
>
> Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
> ---
>  drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 318 ++++++++++++++++++++++++
>  drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |   6 +
>  drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   2 +
>  3 files changed, 326 insertions(+)
>
> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
> index 9c1a68c..9b5d0d0 100644
> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
> @@ -23,6 +23,7 @@
>
>  #include "hibmc_drm_drv.h"
>  #include "hibmc_drm_regs.h"
> +#include "hibmc_drm_de.h"
>  #include "hibmc_drm_power.h"

nit: alphabetize

>
>  /* ---------------------------------------------------------------------- */

Remove

> @@ -168,3 +169,320 @@ int hibmc_plane_init(struct hibmc_drm_device *hidev)
>         drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
>         return 0;
>  }
> +
> +static void hibmc_crtc_enable(struct drm_crtc *crtc)
> +{
> +       unsigned int reg;
> +       /* power mode 0 is default. */

This comment seems to be in the wrong place

> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
> +
> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
> +
> +       /* Enable display power gate & LOCALMEM power gate*/
> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
> +       hibmc_set_current_gate(hidev, reg);
> +       drm_crtc_vblank_on(crtc);
> +}
> +
> +static void hibmc_crtc_disable(struct drm_crtc *crtc)
> +{
> +       unsigned int reg;
> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
> +
> +       drm_crtc_vblank_off(crtc);
> +
> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_SLEEP);
> +
> +       /* Enable display power gate & LOCALMEM power gate*/
> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
> +       reg |= HIBMC_CURR_GATE_LOCALMEM(OFF);
> +       reg |= HIBMC_CURR_GATE_DISPLAY(OFF);
> +       hibmc_set_current_gate(hidev, reg);
> +}
> +
> +static int hibmc_crtc_atomic_check(struct drm_crtc *crtc,
> +                                  struct drm_crtc_state *state)
> +{
> +       return 0;
> +}

Caller NULL-checks, no need for stub

> +
> +static unsigned int format_pll_reg(void)
> +{
> +       unsigned int pllreg = 0;
> +       struct panel_pll pll = {0};
> +
> +       /* Note that all PLL's have the same format. Here,
> +        * we just use Panel PLL parameter to work out the bit
> +        * fields in the register.On returning a 32 bit number, the value can
> +        * be applied to any PLL in the calling function.
> +        */
> +       pllreg |= HIBMC_PLL_CTRL_BYPASS(OFF) & HIBMC_PLL_CTRL_BYPASS_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_POWER(ON) & HIBMC_PLL_CTRL_POWER_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_INPUT(OSC) & HIBMC_PLL_CTRL_INPUT_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_POD(pll.POD) & HIBMC_PLL_CTRL_POD_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_OD(pll.OD) & HIBMC_PLL_CTRL_OD_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_N(pll.N) & HIBMC_PLL_CTRL_N_MASK;
> +       pllreg |= HIBMC_PLL_CTRL_M(pll.M) & HIBMC_PLL_CTRL_M_MASK;
> +
> +       return pllreg;
> +}
> +
> +static void set_vclock_hisilicon(struct drm_device *dev, unsigned long pll)
> +{
> +       unsigned long tmp0, tmp1;
> +       struct hibmc_drm_device *hidev = dev->dev_private;
> +
> +       /* 1. outer_bypass_n=0 */
> +       tmp0 = readl(hidev->mmio + CRT_PLL1_HS);
> +       tmp0 &= 0xBFFFFFFF;
> +       writel(tmp0, hidev->mmio + CRT_PLL1_HS);
> +
> +       /* 2. pll_pd=1?inter_bypass=1 */
> +       writel(0x21000000, hidev->mmio + CRT_PLL1_HS);
> +
> +       /* 3. config pll */
> +       writel(pll, hidev->mmio + CRT_PLL1_HS);
> +
> +       /* 4. delay  */
> +       mdelay(1);

These should be usleep_range() see
https://www.kernel.org/doc/Documentation/timers/timers-howto.txt

> +
> +       /* 5. pll_pd =0 */
> +       tmp1 = pll & ~0x01000000;
> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
> +
> +       /* 6. delay  */
> +       mdelay(1);
> +
> +       /* 7. inter_bypass=0 */
> +       tmp1 &= ~0x20000000;
> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
> +
> +       /* 8. delay  */
> +       mdelay(1);
> +
> +       /* 9. outer_bypass_n=1 */
> +       tmp1 |= 0x40000000;
> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);

This function is a whole lot of magic. Any chance you can pull the
values out into #defines?

> +}
> +
> +/* This function takes care the extra registers and bit fields required to

nit: multi-line comments have a leading /* line with the comment
starting on the following line

applies below as well


> + *setup a mode in board.

nit: space between * and comment, ie: * setup a mode in board

applies to the rest of the comment too


> + *Explanation about Display Control register:
> + *FPGA only supports 7 predefined pixel clocks, and clock select is
> + *in bit 4:0 of new register 0x802a8.
> + */
> +static unsigned int display_ctrl_adjust(struct drm_device *dev,
> +                                       struct drm_display_mode *mode,
> +                                       unsigned int ctrl)
> +{
> +       unsigned long x, y;
> +       unsigned long pll1; /* bit[31:0] of PLL */
> +       unsigned long pll2; /* bit[63:32] of PLL */
> +       struct hibmc_drm_device *hidev = dev->dev_private;
> +
> +       x = mode->hdisplay;
> +       y = mode->vdisplay;
> +
> +       /* Hisilicon has to set up a new register for PLL control
> +        *(CRT_PLL1_HS & CRT_PLL2_HS).
> +        */
> +       if (x == 800 && y == 600) {
> +               pll1 = CRT_PLL1_HS_40MHZ;
> +               pll2 = CRT_PLL2_HS_40MHZ;
> +       } else if (x == 1024 && y == 768) {
> +               pll1 = CRT_PLL1_HS_65MHZ;
> +               pll2 = CRT_PLL2_HS_65MHZ;
> +       } else if (x == 1152 && y == 864) {
> +               pll1 = CRT_PLL1_HS_80MHZ_1152;
> +               pll2 = CRT_PLL2_HS_80MHZ;
> +       } else if (x == 1280 && y == 768) {
> +               pll1 = CRT_PLL1_HS_80MHZ;
> +               pll2 = CRT_PLL2_HS_80MHZ;
> +       } else if (x == 1280 && y == 720) {
> +               pll1 = CRT_PLL1_HS_74MHZ;
> +               pll2 = CRT_PLL2_HS_74MHZ;
> +       } else if (x == 1280 && y == 960) {
> +               pll1 = CRT_PLL1_HS_108MHZ;
> +               pll2 = CRT_PLL2_HS_108MHZ;
> +       } else if (x == 1280 && y == 1024) {
> +               pll1 = CRT_PLL1_HS_108MHZ;
> +               pll2 = CRT_PLL2_HS_108MHZ;
> +       } else if (x == 1600 && y == 1200) {
> +               pll1 = CRT_PLL1_HS_162MHZ;
> +               pll2 = CRT_PLL2_HS_162MHZ;
> +       } else if (x == 1920 && y == 1080) {
> +               pll1 = CRT_PLL1_HS_148MHZ;
> +               pll2 = CRT_PLL2_HS_148MHZ;
> +       } else if (x == 1920 && y == 1200) {
> +               pll1 = CRT_PLL1_HS_193MHZ;
> +               pll2 = CRT_PLL2_HS_193MHZ;
> +       } else /* default to VGA clock */ {
> +               pll1 = CRT_PLL1_HS_25MHZ;
> +               pll2 = CRT_PLL2_HS_25MHZ;
> +       }

This seems like something that should be checked in atomic_check so
you can be sure the mode is supported.

It would also be nice to pull this out into a separate function (and a
lookup table if you're feeling adventurous)

> +
> +       writel(pll2, hidev->mmio + CRT_PLL2_HS);
> +       set_vclock_hisilicon(dev, pll1);
> +
> +       /* Hisilicon has to set up the top-left and bottom-right
> +        * registers as well.
> +        * Note that normal chip only use those two register for
> +        * auto-centering mode.
> +        */
> +       writel((HIBMC_CRT_AUTO_CENTERING_TL_TOP(0) &
> +               HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK) |
> +              (HIBMC_CRT_AUTO_CENTERING_TL_LEFT(0) &
> +               HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK),
> +              hidev->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
> +
> +       writel((HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(y - 1) &
> +               HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
> +              (HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x - 1) &
> +               HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK),
> +               hidev->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
> +
> +       /* Assume common fields in ctrl have been properly set before
> +        * calling this function.
> +        * This function only sets the extra fields in ctrl.
> +        */
> +
> +       /* Set bit 25 of display controller: Select CRT or VGA clock */
> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
> +
> +       ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(CRTSELECT_CRT);
> +
> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL, CRTSELECT, CRT);*/

What's the deal with this commented code?

> +
> +       /* Set bit 14 of display controller */
> +       /*ctrl &= FIELD_CLEAR(HIBMC_CRT_DISP_CTL, CLOCK_PHASE);*/
> +
> +       /* clock_phase_polarity is 0 */
> +       ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(PHASE_ACTIVE_HIGH);
> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL,*/
> +       /*CLOCK_PHASE, ACTIVE_HIGH);*/

Here too...

> +
> +       writel(ctrl, hidev->mmio + HIBMC_CRT_DISP_CTL);
> +
> +       return ctrl;
> +}
> +
> +static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +       unsigned int val;
> +       struct drm_display_mode *mode = &crtc->state->mode;
> +       struct drm_device *dev = crtc->dev;
> +       struct hibmc_drm_device *hidev = dev->dev_private;
> +
> +       writel(format_pll_reg(), hidev->mmio + HIBMC_CRT_PLL_CTRL);
> +       writel((HIBMC_CRT_HORZ_TOTAL_TOTAL(mode->htotal - 1) &
> +               HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK) |
> +               (HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(mode->hdisplay - 1) &
> +               HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK),

You could probably macroize this code to make it more readable

> +               hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
> +
> +       writel((HIBMC_CRT_HORZ_SYNC_WIDTH(mode->hsync_end - mode->hsync_start)
> +               & HIBMC_CRT_HORZ_SYNC_WIDTH_MASK) |
> +               (HIBMC_CRT_HORZ_SYNC_START(mode->hsync_start - 1)
> +               & HIBMC_CRT_HORZ_SYNC_START_MASK),
> +               hidev->mmio + HIBMC_CRT_HORZ_SYNC);
> +
> +       writel((HIBMC_CRT_VERT_TOTAL_TOTAL(mode->vtotal - 1) &
> +               HIBMC_CRT_VERT_TOTAL_TOTAL_MASK) |
> +               (HIBMC_CRT_VERT_TOTAL_DISPLAY_END(mode->vdisplay - 1) &
> +               HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK),
> +               hidev->mmio + HIBMC_CRT_VERT_TOTAL);
> +
> +       writel((HIBMC_CRT_VERT_SYNC_HEIGHT(mode->vsync_end - mode->vsync_start)
> +               & HIBMC_CRT_VERT_SYNC_HEIGHT_MASK) |
> +              (HIBMC_CRT_VERT_SYNC_START(mode->vsync_start - 1) &
> +               HIBMC_CRT_VERT_SYNC_START_MASK),
> +               hidev->mmio + HIBMC_CRT_VERT_SYNC);
> +
> +       val = HIBMC_CRT_DISP_CTL_VSYNC_PHASE(0) &
> +             HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK;
> +       val |= HIBMC_CRT_DISP_CTL_HSYNC_PHASE(0) &
> +              HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK;
> +       val |= HIBMC_CRT_DISP_CTL_TIMING(ENABLE);
> +       val |= HIBMC_CRT_DISP_CTL_PLANE(ENABLE);
> +
> +       display_ctrl_adjust(dev, mode, val);
> +}
> +
> +static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                   struct drm_crtc_state *old_state)
> +{
> +       unsigned int reg;
> +       struct drm_device *dev = crtc->dev;
> +       struct hibmc_drm_device *hidev = dev->dev_private;
> +
> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
> +
> +       /* Enable display power gate & LOCALMEM power gate*/
> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
> +       hibmc_set_current_gate(hidev, reg);
> +
> +       /* We can add more initialization as needed. */
> +}
> +
> +static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
> +                                   struct drm_crtc_state *old_state)
> +
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&crtc->dev->event_lock, flags);
> +       if (crtc->state->event)
> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +       crtc->state->event = NULL;
> +
> +       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
> +}
> +
> +/* These provide the minimum set of functions required to handle a CRTC */

nit: don't need this comment

> +static const struct drm_crtc_funcs hibmc_crtc_funcs = {
> +       .page_flip = drm_atomic_helper_page_flip,
> +       .set_config = drm_atomic_helper_set_config,
> +       .destroy = drm_crtc_cleanup,
> +       .reset = drm_atomic_helper_crtc_reset,
> +       .atomic_duplicate_state =  drm_atomic_helper_crtc_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
> +       .enable         = hibmc_crtc_enable,
> +       .disable        = hibmc_crtc_disable,
> +       .mode_set_nofb  = hibmc_crtc_mode_set_nofb,
> +       .atomic_check   = hibmc_crtc_atomic_check,
> +       .atomic_begin   = hibmc_crtc_atomic_begin,
> +       .atomic_flush   = hibmc_crtc_atomic_flush,
> +};
> +
> +int hibmc_crtc_init(struct hibmc_drm_device *hidev)
> +{
> +       struct drm_device *dev = hidev->dev;
> +       struct drm_crtc *crtc = &hidev->crtc;
> +       struct drm_plane *plane = &hidev->plane;
> +       int ret;
> +
> +       ret = drm_crtc_init_with_planes(dev, crtc, plane,
> +                                       NULL, &hibmc_crtc_funcs, NULL);
> +       if (ret) {
> +               DRM_ERROR("failed to init crtc.\n");

print return code

> +               return ret;
> +       }
> +
> +       drm_mode_crtc_set_gamma_size(crtc, 256);

check return code

> +       drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
> +       return 0;
> +}
> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> index 7d96583..303cd36 100644
> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
> @@ -119,6 +119,12 @@ static int hibmc_kms_init(struct hibmc_drm_device *hidev)
>                 return ret;
>         }
>
> +       ret = hibmc_crtc_init(hidev);
> +       if (ret) {
> +               DRM_ERROR("failed to init crtc.\n");
> +               return ret;
> +       }

Typically the plane is initialized internally in the crtc driver. I
think this is a good design pattern, and you should probably use it.

So how about squashing this down with the plane patch and keeping the
plane inside hibmc_drm_de.c?

> +
>         return 0;
>  }
>
> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
> index 49e39d2..5731ec2 100644
> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
> @@ -46,6 +46,7 @@ struct hibmc_drm_device {
>         /* drm */
>         struct drm_device  *dev;
>         struct drm_plane plane;

I don't think you should be keeping track of plane here. plane is only
used in the crtc init, which should be addressed by the previous
comment.


> +       struct drm_crtc crtc;

crtc is only used in the irq handler, so you could remove this here
and just call drm_handle_vblank(dev, 0) in the handler.


>         bool mode_config_initialized;
>
>         /* ttm */
> @@ -85,6 +86,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
>  #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
>
>  int hibmc_plane_init(struct hibmc_drm_device *hidev);
> +int hibmc_crtc_init(struct hibmc_drm_device *hidev);
>  int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
>  void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
>
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Rongrong Zou Nov. 12, 2016, 10:19 a.m. UTC | #2
在 2016/11/11 6:14, Sean Paul 写道:
> On Fri, Oct 28, 2016 at 3:27 AM, Rongrong Zou <zourongrong@gmail.com> wrote:
>> Add crtc funcs and helper funcs for DE.
>>
>> Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
>> ---
>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 318 ++++++++++++++++++++++++
>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |   6 +
>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   2 +
>>   3 files changed, 326 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>> index 9c1a68c..9b5d0d0 100644
>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>> @@ -23,6 +23,7 @@
>>
>>   #include "hibmc_drm_drv.h"
>>   #include "hibmc_drm_regs.h"
>> +#include "hibmc_drm_de.h"
>>   #include "hibmc_drm_power.h"
>
> nit: alphabetize

ok, thanks.

>
>>
>>   /* ---------------------------------------------------------------------- */
>
> Remove

will do, thanks.

>
>> @@ -168,3 +169,320 @@ int hibmc_plane_init(struct hibmc_drm_device *hidev)
>>          drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
>>          return 0;
>>   }
>> +
>> +static void hibmc_crtc_enable(struct drm_crtc *crtc)
>> +{
>> +       unsigned int reg;
>> +       /* power mode 0 is default. */
>
> This comment seems to be in the wrong place

will remove it, thanks.

>
>> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
>> +
>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
>> +
>> +       /* Enable display power gate & LOCALMEM power gate*/
>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
>> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
>> +       hibmc_set_current_gate(hidev, reg);
>> +       drm_crtc_vblank_on(crtc);
>> +}
>> +
>> +static void hibmc_crtc_disable(struct drm_crtc *crtc)
>> +{
>> +       unsigned int reg;
>> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
>> +
>> +       drm_crtc_vblank_off(crtc);
>> +
>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_SLEEP);
>> +
>> +       /* Enable display power gate & LOCALMEM power gate*/
>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(OFF);
>> +       reg |= HIBMC_CURR_GATE_DISPLAY(OFF);
>> +       hibmc_set_current_gate(hidev, reg);
>> +}
>> +
>> +static int hibmc_crtc_atomic_check(struct drm_crtc *crtc,
>> +                                  struct drm_crtc_state *state)
>> +{
>> +       return 0;
>> +}
>
> Caller NULL-checks, no need for stub

thanks for pointing it out.

>
>> +
>> +static unsigned int format_pll_reg(void)
>> +{
>> +       unsigned int pllreg = 0;
>> +       struct panel_pll pll = {0};
>> +
>> +       /* Note that all PLL's have the same format. Here,
>> +        * we just use Panel PLL parameter to work out the bit
>> +        * fields in the register.On returning a 32 bit number, the value can
>> +        * be applied to any PLL in the calling function.
>> +        */
>> +       pllreg |= HIBMC_PLL_CTRL_BYPASS(OFF) & HIBMC_PLL_CTRL_BYPASS_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_POWER(ON) & HIBMC_PLL_CTRL_POWER_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_INPUT(OSC) & HIBMC_PLL_CTRL_INPUT_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_POD(pll.POD) & HIBMC_PLL_CTRL_POD_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_OD(pll.OD) & HIBMC_PLL_CTRL_OD_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_N(pll.N) & HIBMC_PLL_CTRL_N_MASK;
>> +       pllreg |= HIBMC_PLL_CTRL_M(pll.M) & HIBMC_PLL_CTRL_M_MASK;
>> +
>> +       return pllreg;
>> +}
>> +
>> +static void set_vclock_hisilicon(struct drm_device *dev, unsigned long pll)
>> +{
>> +       unsigned long tmp0, tmp1;
>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>> +
>> +       /* 1. outer_bypass_n=0 */
>> +       tmp0 = readl(hidev->mmio + CRT_PLL1_HS);
>> +       tmp0 &= 0xBFFFFFFF;
>> +       writel(tmp0, hidev->mmio + CRT_PLL1_HS);
>> +
>> +       /* 2. pll_pd=1?inter_bypass=1 */
>> +       writel(0x21000000, hidev->mmio + CRT_PLL1_HS);
>> +
>> +       /* 3. config pll */
>> +       writel(pll, hidev->mmio + CRT_PLL1_HS);
>> +
>> +       /* 4. delay  */
>> +       mdelay(1);
>
> These should be usleep_range() see
> https://www.kernel.org/doc/Documentation/timers/timers-howto.txt

This looks better to me. i think a 'usleep_range(1000, 2000)' is ok.

>
>> +
>> +       /* 5. pll_pd =0 */
>> +       tmp1 = pll & ~0x01000000;
>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>> +
>> +       /* 6. delay  */
>> +       mdelay(1);
>> +
>> +       /* 7. inter_bypass=0 */
>> +       tmp1 &= ~0x20000000;
>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>> +
>> +       /* 8. delay  */
>> +       mdelay(1);
>> +
>> +       /* 9. outer_bypass_n=1 */
>> +       tmp1 |= 0x40000000;
>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>
> This function is a whole lot of magic. Any chance you can pull the
> values out into #defines?

will do. thanks.

>
>> +}
>> +
>> +/* This function takes care the extra registers and bit fields required to
>
> nit: multi-line comments have a leading /* line with the comment
> starting on the following line

thanks for pointing it out.

>
> applies below as well
>
>
>> + *setup a mode in board.
>
> nit: space between * and comment, ie: * setup a mode in board

understood, thanks.

>
> applies to the rest of the comment too
>
>
>> + *Explanation about Display Control register:
>> + *FPGA only supports 7 predefined pixel clocks, and clock select is
>> + *in bit 4:0 of new register 0x802a8.
>> + */
>> +static unsigned int display_ctrl_adjust(struct drm_device *dev,
>> +                                       struct drm_display_mode *mode,
>> +                                       unsigned int ctrl)
>> +{
>> +       unsigned long x, y;
>> +       unsigned long pll1; /* bit[31:0] of PLL */
>> +       unsigned long pll2; /* bit[63:32] of PLL */
>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>> +
>> +       x = mode->hdisplay;
>> +       y = mode->vdisplay;
>> +
>> +       /* Hisilicon has to set up a new register for PLL control
>> +        *(CRT_PLL1_HS & CRT_PLL2_HS).
>> +        */
>> +       if (x == 800 && y == 600) {
>> +               pll1 = CRT_PLL1_HS_40MHZ;
>> +               pll2 = CRT_PLL2_HS_40MHZ;
>> +       } else if (x == 1024 && y == 768) {
>> +               pll1 = CRT_PLL1_HS_65MHZ;
>> +               pll2 = CRT_PLL2_HS_65MHZ;
>> +       } else if (x == 1152 && y == 864) {
>> +               pll1 = CRT_PLL1_HS_80MHZ_1152;
>> +               pll2 = CRT_PLL2_HS_80MHZ;
>> +       } else if (x == 1280 && y == 768) {
>> +               pll1 = CRT_PLL1_HS_80MHZ;
>> +               pll2 = CRT_PLL2_HS_80MHZ;
>> +       } else if (x == 1280 && y == 720) {
>> +               pll1 = CRT_PLL1_HS_74MHZ;
>> +               pll2 = CRT_PLL2_HS_74MHZ;
>> +       } else if (x == 1280 && y == 960) {
>> +               pll1 = CRT_PLL1_HS_108MHZ;
>> +               pll2 = CRT_PLL2_HS_108MHZ;
>> +       } else if (x == 1280 && y == 1024) {
>> +               pll1 = CRT_PLL1_HS_108MHZ;
>> +               pll2 = CRT_PLL2_HS_108MHZ;
>> +       } else if (x == 1600 && y == 1200) {
>> +               pll1 = CRT_PLL1_HS_162MHZ;
>> +               pll2 = CRT_PLL2_HS_162MHZ;
>> +       } else if (x == 1920 && y == 1080) {
>> +               pll1 = CRT_PLL1_HS_148MHZ;
>> +               pll2 = CRT_PLL2_HS_148MHZ;
>> +       } else if (x == 1920 && y == 1200) {
>> +               pll1 = CRT_PLL1_HS_193MHZ;
>> +               pll2 = CRT_PLL2_HS_193MHZ;
>> +       } else /* default to VGA clock */ {
>> +               pll1 = CRT_PLL1_HS_25MHZ;
>> +               pll2 = CRT_PLL2_HS_25MHZ;
>> +       }
>
> This seems like something that should be checked in atomic_check so
> you can be sure the mode is supported.
>
> It would also be nice to pull this out into a separate function (and a
> lookup table if you're feeling adventurous)

a lookup table seems good, thanks.

>
>> +
>> +       writel(pll2, hidev->mmio + CRT_PLL2_HS);
>> +       set_vclock_hisilicon(dev, pll1);
>> +
>> +       /* Hisilicon has to set up the top-left and bottom-right
>> +        * registers as well.
>> +        * Note that normal chip only use those two register for
>> +        * auto-centering mode.
>> +        */
>> +       writel((HIBMC_CRT_AUTO_CENTERING_TL_TOP(0) &
>> +               HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK) |
>> +              (HIBMC_CRT_AUTO_CENTERING_TL_LEFT(0) &
>> +               HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK),
>> +              hidev->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
>> +
>> +       writel((HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(y - 1) &
>> +               HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
>> +              (HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x - 1) &
>> +               HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK),
>> +               hidev->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
>> +
>> +       /* Assume common fields in ctrl have been properly set before
>> +        * calling this function.
>> +        * This function only sets the extra fields in ctrl.
>> +        */
>> +
>> +       /* Set bit 25 of display controller: Select CRT or VGA clock */
>> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
>> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
>> +
>> +       ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(CRTSELECT_CRT);
>> +
>> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL, CRTSELECT, CRT);*/
>
> What's the deal with this commented code?

sorry, will clean up.

>
>> +
>> +       /* Set bit 14 of display controller */
>> +       /*ctrl &= FIELD_CLEAR(HIBMC_CRT_DISP_CTL, CLOCK_PHASE);*/
>> +
>> +       /* clock_phase_polarity is 0 */
>> +       ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(PHASE_ACTIVE_HIGH);
>> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL,*/
>> +       /*CLOCK_PHASE, ACTIVE_HIGH);*/
>
> Here too...

ditto.

>
>> +
>> +       writel(ctrl, hidev->mmio + HIBMC_CRT_DISP_CTL);
>> +
>> +       return ctrl;
>> +}
>> +
>> +static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
>> +{
>> +       unsigned int val;
>> +       struct drm_display_mode *mode = &crtc->state->mode;
>> +       struct drm_device *dev = crtc->dev;
>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>> +
>> +       writel(format_pll_reg(), hidev->mmio + HIBMC_CRT_PLL_CTRL);
>> +       writel((HIBMC_CRT_HORZ_TOTAL_TOTAL(mode->htotal - 1) &
>> +               HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK) |
>> +               (HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(mode->hdisplay - 1) &
>> +               HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK),
>
> You could probably macroize this code to make it more readable


	#define HIBMC_FIELD(field, value) (field(value) & filed##_MASK)

	writel(HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_TOTAL, mode->htotal - 1) |
	       HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_DISPLAY_END, mode->hdisplay - 1),
	       hidev->mmio + HIBMC_CRT_HORZ_TOTAL);

Is above ok?
	


>
>> +               hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
>> +
>> +       writel((HIBMC_CRT_HORZ_SYNC_WIDTH(mode->hsync_end - mode->hsync_start)
>> +               & HIBMC_CRT_HORZ_SYNC_WIDTH_MASK) |
>> +               (HIBMC_CRT_HORZ_SYNC_START(mode->hsync_start - 1)
>> +               & HIBMC_CRT_HORZ_SYNC_START_MASK),
>> +               hidev->mmio + HIBMC_CRT_HORZ_SYNC);
>> +
>> +       writel((HIBMC_CRT_VERT_TOTAL_TOTAL(mode->vtotal - 1) &
>> +               HIBMC_CRT_VERT_TOTAL_TOTAL_MASK) |
>> +               (HIBMC_CRT_VERT_TOTAL_DISPLAY_END(mode->vdisplay - 1) &
>> +               HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK),
>> +               hidev->mmio + HIBMC_CRT_VERT_TOTAL);
>> +
>> +       writel((HIBMC_CRT_VERT_SYNC_HEIGHT(mode->vsync_end - mode->vsync_start)
>> +               & HIBMC_CRT_VERT_SYNC_HEIGHT_MASK) |
>> +              (HIBMC_CRT_VERT_SYNC_START(mode->vsync_start - 1) &
>> +               HIBMC_CRT_VERT_SYNC_START_MASK),
>> +               hidev->mmio + HIBMC_CRT_VERT_SYNC);
>> +
>> +       val = HIBMC_CRT_DISP_CTL_VSYNC_PHASE(0) &
>> +             HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK;
>> +       val |= HIBMC_CRT_DISP_CTL_HSYNC_PHASE(0) &
>> +              HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK;
>> +       val |= HIBMC_CRT_DISP_CTL_TIMING(ENABLE);
>> +       val |= HIBMC_CRT_DISP_CTL_PLANE(ENABLE);
>> +
>> +       display_ctrl_adjust(dev, mode, val);
>> +}
>> +
>> +static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
>> +                                   struct drm_crtc_state *old_state)
>> +{
>> +       unsigned int reg;
>> +       struct drm_device *dev = crtc->dev;
>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>> +
>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
>> +
>> +       /* Enable display power gate & LOCALMEM power gate*/
>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
>> +       hibmc_set_current_gate(hidev, reg);
>> +
>> +       /* We can add more initialization as needed. */
>> +}
>> +
>> +static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
>> +                                   struct drm_crtc_state *old_state)
>> +
>> +{
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&crtc->dev->event_lock, flags);
>> +       if (crtc->state->event)
>> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
>> +       crtc->state->event = NULL;
>> +
>> +       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
>> +}
>> +
>> +/* These provide the minimum set of functions required to handle a CRTC */
>
> nit: don't need this comment

will delete, thanks.

>
>> +static const struct drm_crtc_funcs hibmc_crtc_funcs = {
>> +       .page_flip = drm_atomic_helper_page_flip,
>> +       .set_config = drm_atomic_helper_set_config,
>> +       .destroy = drm_crtc_cleanup,
>> +       .reset = drm_atomic_helper_crtc_reset,
>> +       .atomic_duplicate_state =  drm_atomic_helper_crtc_duplicate_state,
>> +       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
>> +};
>> +
>> +static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
>> +       .enable         = hibmc_crtc_enable,
>> +       .disable        = hibmc_crtc_disable,
>> +       .mode_set_nofb  = hibmc_crtc_mode_set_nofb,
>> +       .atomic_check   = hibmc_crtc_atomic_check,
>> +       .atomic_begin   = hibmc_crtc_atomic_begin,
>> +       .atomic_flush   = hibmc_crtc_atomic_flush,
>> +};
>> +
>> +int hibmc_crtc_init(struct hibmc_drm_device *hidev)
>> +{
>> +       struct drm_device *dev = hidev->dev;
>> +       struct drm_crtc *crtc = &hidev->crtc;
>> +       struct drm_plane *plane = &hidev->plane;
>> +       int ret;
>> +
>> +       ret = drm_crtc_init_with_planes(dev, crtc, plane,
>> +                                       NULL, &hibmc_crtc_funcs, NULL);
>> +       if (ret) {
>> +               DRM_ERROR("failed to init crtc.\n");
>
> print return code

agreed, thanks.

>
>> +               return ret;
>> +       }
>> +
>> +       drm_mode_crtc_set_gamma_size(crtc, 256);
>
> check return code

agreed though none of other drivers has done this,
thanks.

>
>> +       drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
>> +       return 0;
>> +}
>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>> index 7d96583..303cd36 100644
>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>> @@ -119,6 +119,12 @@ static int hibmc_kms_init(struct hibmc_drm_device *hidev)
>>                  return ret;
>>          }
>>
>> +       ret = hibmc_crtc_init(hidev);
>> +       if (ret) {
>> +               DRM_ERROR("failed to init crtc.\n");
>> +               return ret;
>> +       }
>
> Typically the plane is initialized internally in the crtc driver. I
> think this is a good design pattern, and you should probably use it.
>
> So how about squashing this down with the plane patch and keeping the
> plane inside hibmc_drm_de.c?

understood after i looked at intel_display.c, this file will be merged
with patch 4/9, and the tile will be: 'drm/hisilicon/hibmc: Add display
engine'.

>
>> +
>>          return 0;
>>   }
>>
>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>> index 49e39d2..5731ec2 100644
>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>> @@ -46,6 +46,7 @@ struct hibmc_drm_device {
>>          /* drm */
>>          struct drm_device  *dev;
>>          struct drm_plane plane;
>
> I don't think you should be keeping track of plane here. plane is only
> used in the crtc init, which should be addressed by the previous
> comment.

so allocate with devm_kzalloc(sizeof(*plane)) and remove it from
hibmc_drm_device?

>
>
>> +       struct drm_crtc crtc;
>
> crtc is only used in the irq handler, so you could remove this here
> and just call drm_handle_vblank(dev, 0) in the handler.

so allocate with devm_kzalloc(sizeof(*crtc)) and remove it from
hibmc_drm_device, when driver unload drm_crtc_cleanup() will be
called and finally memory will be freed before quit.

>
>
>>          bool mode_config_initialized;
>>
>>          /* ttm */
>> @@ -85,6 +86,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
>>   #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
>>
>>   int hibmc_plane_init(struct hibmc_drm_device *hidev);
>> +int hibmc_crtc_init(struct hibmc_drm_device *hidev);
>>   int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
>>   void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
>>
>> --
>> 1.9.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> _______________________________________________
> linuxarm mailing list
> linuxarm@huawei.com
> http://rnd-openeuler.huawei.com/mailman/listinfo/linuxarm
>
> .
>
Sean Paul Nov. 14, 2016, 5:10 p.m. UTC | #3
On Sat, Nov 12, 2016 at 5:19 AM, Rongrong Zou <zourongrong@huawei.com> wrote:
> 在 2016/11/11 6:14, Sean Paul 写道:
>>
>> On Fri, Oct 28, 2016 at 3:27 AM, Rongrong Zou <zourongrong@gmail.com>
>> wrote:
>>>
>>> Add crtc funcs and helper funcs for DE.
>>>
>>> Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
>>> ---
>>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 318
>>> ++++++++++++++++++++++++
>>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |   6 +
>>>   drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   2 +
>>>   3 files changed, 326 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>>> b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>>> index 9c1a68c..9b5d0d0 100644
>>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
>>> @@ -23,6 +23,7 @@
>>>
>>>   #include "hibmc_drm_drv.h"
>>>   #include "hibmc_drm_regs.h"
>>> +#include "hibmc_drm_de.h"
>>>   #include "hibmc_drm_power.h"
>>
>>
>> nit: alphabetize
>
>
> ok, thanks.
>
>>
>>>
>>>   /*
>>> ---------------------------------------------------------------------- */
>>
>>
>> Remove
>
>
> will do, thanks.
>
>>
>>> @@ -168,3 +169,320 @@ int hibmc_plane_init(struct hibmc_drm_device
>>> *hidev)
>>>          drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
>>>          return 0;
>>>   }
>>> +
>>> +static void hibmc_crtc_enable(struct drm_crtc *crtc)
>>> +{
>>> +       unsigned int reg;
>>> +       /* power mode 0 is default. */
>>
>>
>> This comment seems to be in the wrong place
>
>
> will remove it, thanks.
>
>
>>
>>> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
>>> +
>>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
>>> +
>>> +       /* Enable display power gate & LOCALMEM power gate*/
>>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
>>> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
>>> +       hibmc_set_current_gate(hidev, reg);
>>> +       drm_crtc_vblank_on(crtc);
>>> +}
>>> +
>>> +static void hibmc_crtc_disable(struct drm_crtc *crtc)
>>> +{
>>> +       unsigned int reg;
>>> +       struct hibmc_drm_device *hidev = crtc->dev->dev_private;
>>> +
>>> +       drm_crtc_vblank_off(crtc);
>>> +
>>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_SLEEP);
>>> +
>>> +       /* Enable display power gate & LOCALMEM power gate*/
>>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(OFF);
>>> +       reg |= HIBMC_CURR_GATE_DISPLAY(OFF);
>>> +       hibmc_set_current_gate(hidev, reg);
>>> +}
>>> +
>>> +static int hibmc_crtc_atomic_check(struct drm_crtc *crtc,
>>> +                                  struct drm_crtc_state *state)
>>> +{
>>> +       return 0;
>>> +}
>>
>>
>> Caller NULL-checks, no need for stub
>
>
> thanks for pointing it out.
>
>
>>
>>> +
>>> +static unsigned int format_pll_reg(void)
>>> +{
>>> +       unsigned int pllreg = 0;
>>> +       struct panel_pll pll = {0};
>>> +
>>> +       /* Note that all PLL's have the same format. Here,
>>> +        * we just use Panel PLL parameter to work out the bit
>>> +        * fields in the register.On returning a 32 bit number, the value
>>> can
>>> +        * be applied to any PLL in the calling function.
>>> +        */
>>> +       pllreg |= HIBMC_PLL_CTRL_BYPASS(OFF) &
>>> HIBMC_PLL_CTRL_BYPASS_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_POWER(ON) & HIBMC_PLL_CTRL_POWER_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_INPUT(OSC) & HIBMC_PLL_CTRL_INPUT_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_POD(pll.POD) & HIBMC_PLL_CTRL_POD_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_OD(pll.OD) & HIBMC_PLL_CTRL_OD_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_N(pll.N) & HIBMC_PLL_CTRL_N_MASK;
>>> +       pllreg |= HIBMC_PLL_CTRL_M(pll.M) & HIBMC_PLL_CTRL_M_MASK;
>>> +
>>> +       return pllreg;
>>> +}
>>> +
>>> +static void set_vclock_hisilicon(struct drm_device *dev, unsigned long
>>> pll)
>>> +{
>>> +       unsigned long tmp0, tmp1;
>>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>>> +
>>> +       /* 1. outer_bypass_n=0 */
>>> +       tmp0 = readl(hidev->mmio + CRT_PLL1_HS);
>>> +       tmp0 &= 0xBFFFFFFF;
>>> +       writel(tmp0, hidev->mmio + CRT_PLL1_HS);
>>> +
>>> +       /* 2. pll_pd=1?inter_bypass=1 */
>>> +       writel(0x21000000, hidev->mmio + CRT_PLL1_HS);
>>> +
>>> +       /* 3. config pll */
>>> +       writel(pll, hidev->mmio + CRT_PLL1_HS);
>>> +
>>> +       /* 4. delay  */
>>> +       mdelay(1);
>>
>>
>> These should be usleep_range() see
>> https://www.kernel.org/doc/Documentation/timers/timers-howto.txt
>
>
> This looks better to me. i think a 'usleep_range(1000, 2000)' is ok.
>
>>
>>> +
>>> +       /* 5. pll_pd =0 */
>>> +       tmp1 = pll & ~0x01000000;
>>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>>> +
>>> +       /* 6. delay  */
>>> +       mdelay(1);
>>> +
>>> +       /* 7. inter_bypass=0 */
>>> +       tmp1 &= ~0x20000000;
>>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>>> +
>>> +       /* 8. delay  */
>>> +       mdelay(1);
>>> +
>>> +       /* 9. outer_bypass_n=1 */
>>> +       tmp1 |= 0x40000000;
>>> +       writel(tmp1, hidev->mmio + CRT_PLL1_HS);
>>
>>
>> This function is a whole lot of magic. Any chance you can pull the
>> values out into #defines?
>
>
> will do. thanks.
>
>>
>>> +}
>>> +
>>> +/* This function takes care the extra registers and bit fields required
>>> to
>>
>>
>> nit: multi-line comments have a leading /* line with the comment
>> starting on the following line
>
>
> thanks for pointing it out.
>
>>
>> applies below as well
>>
>>
>>> + *setup a mode in board.
>>
>>
>> nit: space between * and comment, ie: * setup a mode in board
>
>
> understood, thanks.
>
>
>>
>> applies to the rest of the comment too
>>
>>
>>> + *Explanation about Display Control register:
>>> + *FPGA only supports 7 predefined pixel clocks, and clock select is
>>> + *in bit 4:0 of new register 0x802a8.
>>> + */
>>> +static unsigned int display_ctrl_adjust(struct drm_device *dev,
>>> +                                       struct drm_display_mode *mode,
>>> +                                       unsigned int ctrl)
>>> +{
>>> +       unsigned long x, y;
>>> +       unsigned long pll1; /* bit[31:0] of PLL */
>>> +       unsigned long pll2; /* bit[63:32] of PLL */
>>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>>> +
>>> +       x = mode->hdisplay;
>>> +       y = mode->vdisplay;
>>> +
>>> +       /* Hisilicon has to set up a new register for PLL control
>>> +        *(CRT_PLL1_HS & CRT_PLL2_HS).
>>> +        */
>>> +       if (x == 800 && y == 600) {
>>> +               pll1 = CRT_PLL1_HS_40MHZ;
>>> +               pll2 = CRT_PLL2_HS_40MHZ;
>>> +       } else if (x == 1024 && y == 768) {
>>> +               pll1 = CRT_PLL1_HS_65MHZ;
>>> +               pll2 = CRT_PLL2_HS_65MHZ;
>>> +       } else if (x == 1152 && y == 864) {
>>> +               pll1 = CRT_PLL1_HS_80MHZ_1152;
>>> +               pll2 = CRT_PLL2_HS_80MHZ;
>>> +       } else if (x == 1280 && y == 768) {
>>> +               pll1 = CRT_PLL1_HS_80MHZ;
>>> +               pll2 = CRT_PLL2_HS_80MHZ;
>>> +       } else if (x == 1280 && y == 720) {
>>> +               pll1 = CRT_PLL1_HS_74MHZ;
>>> +               pll2 = CRT_PLL2_HS_74MHZ;
>>> +       } else if (x == 1280 && y == 960) {
>>> +               pll1 = CRT_PLL1_HS_108MHZ;
>>> +               pll2 = CRT_PLL2_HS_108MHZ;
>>> +       } else if (x == 1280 && y == 1024) {
>>> +               pll1 = CRT_PLL1_HS_108MHZ;
>>> +               pll2 = CRT_PLL2_HS_108MHZ;
>>> +       } else if (x == 1600 && y == 1200) {
>>> +               pll1 = CRT_PLL1_HS_162MHZ;
>>> +               pll2 = CRT_PLL2_HS_162MHZ;
>>> +       } else if (x == 1920 && y == 1080) {
>>> +               pll1 = CRT_PLL1_HS_148MHZ;
>>> +               pll2 = CRT_PLL2_HS_148MHZ;
>>> +       } else if (x == 1920 && y == 1200) {
>>> +               pll1 = CRT_PLL1_HS_193MHZ;
>>> +               pll2 = CRT_PLL2_HS_193MHZ;
>>> +       } else /* default to VGA clock */ {
>>> +               pll1 = CRT_PLL1_HS_25MHZ;
>>> +               pll2 = CRT_PLL2_HS_25MHZ;
>>> +       }
>>
>>
>> This seems like something that should be checked in atomic_check so
>> you can be sure the mode is supported.
>>
>> It would also be nice to pull this out into a separate function (and a
>> lookup table if you're feeling adventurous)
>
>
> a lookup table seems good, thanks.
>
>
>>
>>> +
>>> +       writel(pll2, hidev->mmio + CRT_PLL2_HS);
>>> +       set_vclock_hisilicon(dev, pll1);
>>> +
>>> +       /* Hisilicon has to set up the top-left and bottom-right
>>> +        * registers as well.
>>> +        * Note that normal chip only use those two register for
>>> +        * auto-centering mode.
>>> +        */
>>> +       writel((HIBMC_CRT_AUTO_CENTERING_TL_TOP(0) &
>>> +               HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK) |
>>> +              (HIBMC_CRT_AUTO_CENTERING_TL_LEFT(0) &
>>> +               HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK),
>>> +              hidev->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
>>> +
>>> +       writel((HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(y - 1) &
>>> +               HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
>>> +              (HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x - 1) &
>>> +               HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK),
>>> +               hidev->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
>>> +
>>> +       /* Assume common fields in ctrl have been properly set before
>>> +        * calling this function.
>>> +        * This function only sets the extra fields in ctrl.
>>> +        */
>>> +
>>> +       /* Set bit 25 of display controller: Select CRT or VGA clock */
>>> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
>>> +       ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
>>> +
>>> +       ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(CRTSELECT_CRT);
>>> +
>>> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL, CRTSELECT, CRT);*/
>>
>>
>> What's the deal with this commented code?
>
>
> sorry, will clean up.
>
>>
>>> +
>>> +       /* Set bit 14 of display controller */
>>> +       /*ctrl &= FIELD_CLEAR(HIBMC_CRT_DISP_CTL, CLOCK_PHASE);*/
>>> +
>>> +       /* clock_phase_polarity is 0 */
>>> +       ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(PHASE_ACTIVE_HIGH);
>>> +       /*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL,*/
>>> +       /*CLOCK_PHASE, ACTIVE_HIGH);*/
>>
>>
>> Here too...
>
>
> ditto.
>
>>
>>> +
>>> +       writel(ctrl, hidev->mmio + HIBMC_CRT_DISP_CTL);
>>> +
>>> +       return ctrl;
>>> +}
>>> +
>>> +static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
>>> +{
>>> +       unsigned int val;
>>> +       struct drm_display_mode *mode = &crtc->state->mode;
>>> +       struct drm_device *dev = crtc->dev;
>>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>>> +
>>> +       writel(format_pll_reg(), hidev->mmio + HIBMC_CRT_PLL_CTRL);
>>> +       writel((HIBMC_CRT_HORZ_TOTAL_TOTAL(mode->htotal - 1) &
>>> +               HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK) |
>>> +               (HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(mode->hdisplay - 1) &
>>> +               HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK),
>>
>>
>> You could probably macroize this code to make it more readable
>
>
>
>         #define HIBMC_FIELD(field, value) (field(value) & filed##_MASK)
>
>         writel(HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_TOTAL, mode->htotal - 1) |
>                HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_DISPLAY_END, mode->hdisplay
> - 1),
>                hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
>
> Is above ok?
>

Seems like an improvement.

>
>
>
>>
>>> +               hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
>>> +
>>> +       writel((HIBMC_CRT_HORZ_SYNC_WIDTH(mode->hsync_end -
>>> mode->hsync_start)
>>> +               & HIBMC_CRT_HORZ_SYNC_WIDTH_MASK) |
>>> +               (HIBMC_CRT_HORZ_SYNC_START(mode->hsync_start - 1)
>>> +               & HIBMC_CRT_HORZ_SYNC_START_MASK),
>>> +               hidev->mmio + HIBMC_CRT_HORZ_SYNC);
>>> +
>>> +       writel((HIBMC_CRT_VERT_TOTAL_TOTAL(mode->vtotal - 1) &
>>> +               HIBMC_CRT_VERT_TOTAL_TOTAL_MASK) |
>>> +               (HIBMC_CRT_VERT_TOTAL_DISPLAY_END(mode->vdisplay - 1) &
>>> +               HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK),
>>> +               hidev->mmio + HIBMC_CRT_VERT_TOTAL);
>>> +
>>> +       writel((HIBMC_CRT_VERT_SYNC_HEIGHT(mode->vsync_end -
>>> mode->vsync_start)
>>> +               & HIBMC_CRT_VERT_SYNC_HEIGHT_MASK) |
>>> +              (HIBMC_CRT_VERT_SYNC_START(mode->vsync_start - 1) &
>>> +               HIBMC_CRT_VERT_SYNC_START_MASK),
>>> +               hidev->mmio + HIBMC_CRT_VERT_SYNC);
>>> +
>>> +       val = HIBMC_CRT_DISP_CTL_VSYNC_PHASE(0) &
>>> +             HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK;
>>> +       val |= HIBMC_CRT_DISP_CTL_HSYNC_PHASE(0) &
>>> +              HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK;
>>> +       val |= HIBMC_CRT_DISP_CTL_TIMING(ENABLE);
>>> +       val |= HIBMC_CRT_DISP_CTL_PLANE(ENABLE);
>>> +
>>> +       display_ctrl_adjust(dev, mode, val);
>>> +}
>>> +
>>> +static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
>>> +                                   struct drm_crtc_state *old_state)
>>> +{
>>> +       unsigned int reg;
>>> +       struct drm_device *dev = crtc->dev;
>>> +       struct hibmc_drm_device *hidev = dev->dev_private;
>>> +
>>> +       hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
>>> +
>>> +       /* Enable display power gate & LOCALMEM power gate*/
>>> +       reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
>>> +       reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
>>> +       reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
>>> +       reg |= HIBMC_CURR_GATE_DISPLAY(ON);
>>> +       reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
>>> +       hibmc_set_current_gate(hidev, reg);
>>> +
>>> +       /* We can add more initialization as needed. */
>>> +}
>>> +
>>> +static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
>>> +                                   struct drm_crtc_state *old_state)
>>> +
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       spin_lock_irqsave(&crtc->dev->event_lock, flags);
>>> +       if (crtc->state->event)
>>> +               drm_crtc_send_vblank_event(crtc, crtc->state->event);
>>> +       crtc->state->event = NULL;
>>> +
>>> +       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
>>> +}
>>> +
>>> +/* These provide the minimum set of functions required to handle a CRTC
>>> */
>>
>>
>> nit: don't need this comment
>
>
> will delete, thanks.
>
>
>>
>>> +static const struct drm_crtc_funcs hibmc_crtc_funcs = {
>>> +       .page_flip = drm_atomic_helper_page_flip,
>>> +       .set_config = drm_atomic_helper_set_config,
>>> +       .destroy = drm_crtc_cleanup,
>>> +       .reset = drm_atomic_helper_crtc_reset,
>>> +       .atomic_duplicate_state =
>>> drm_atomic_helper_crtc_duplicate_state,
>>> +       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
>>> +};
>>> +
>>> +static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
>>> +       .enable         = hibmc_crtc_enable,
>>> +       .disable        = hibmc_crtc_disable,
>>> +       .mode_set_nofb  = hibmc_crtc_mode_set_nofb,
>>> +       .atomic_check   = hibmc_crtc_atomic_check,
>>> +       .atomic_begin   = hibmc_crtc_atomic_begin,
>>> +       .atomic_flush   = hibmc_crtc_atomic_flush,
>>> +};
>>> +
>>> +int hibmc_crtc_init(struct hibmc_drm_device *hidev)
>>> +{
>>> +       struct drm_device *dev = hidev->dev;
>>> +       struct drm_crtc *crtc = &hidev->crtc;
>>> +       struct drm_plane *plane = &hidev->plane;
>>> +       int ret;
>>> +
>>> +       ret = drm_crtc_init_with_planes(dev, crtc, plane,
>>> +                                       NULL, &hibmc_crtc_funcs, NULL);
>>> +       if (ret) {
>>> +               DRM_ERROR("failed to init crtc.\n");
>>
>>
>> print return code
>
>
> agreed, thanks.
>
>>
>>> +               return ret;
>>> +       }
>>> +
>>> +       drm_mode_crtc_set_gamma_size(crtc, 256);
>>
>>
>> check return code
>
>
> agreed though none of other drivers has done this,
> thanks.
>
>>
>>> +       drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
>>> +       return 0;
>>> +}
>>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>>> b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>>> index 7d96583..303cd36 100644
>>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
>>> @@ -119,6 +119,12 @@ static int hibmc_kms_init(struct hibmc_drm_device
>>> *hidev)
>>>                  return ret;
>>>          }
>>>
>>> +       ret = hibmc_crtc_init(hidev);
>>> +       if (ret) {
>>> +               DRM_ERROR("failed to init crtc.\n");
>>> +               return ret;
>>> +       }
>>
>>
>> Typically the plane is initialized internally in the crtc driver. I
>> think this is a good design pattern, and you should probably use it.
>>
>> So how about squashing this down with the plane patch and keeping the
>> plane inside hibmc_drm_de.c?
>
>
> understood after i looked at intel_display.c, this file will be merged
> with patch 4/9, and the tile will be: 'drm/hisilicon/hibmc: Add display
> engine'.
>

Great, thanks.

>>
>>> +
>>>          return 0;
>>>   }
>>>
>>> diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>>> b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>>> index 49e39d2..5731ec2 100644
>>> --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>>> +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
>>> @@ -46,6 +46,7 @@ struct hibmc_drm_device {
>>>          /* drm */
>>>          struct drm_device  *dev;
>>>          struct drm_plane plane;
>>
>>
>> I don't think you should be keeping track of plane here. plane is only
>> used in the crtc init, which should be addressed by the previous
>> comment.
>
>
> so allocate with devm_kzalloc(sizeof(*plane)) and remove it from
> hibmc_drm_device?
>
>>
>>
>>> +       struct drm_crtc crtc;
>>
>>
>> crtc is only used in the irq handler, so you could remove this here
>> and just call drm_handle_vblank(dev, 0) in the handler.
>
>
> so allocate with devm_kzalloc(sizeof(*crtc)) and remove it from
> hibmc_drm_device, when driver unload drm_crtc_cleanup() will be
> called and finally memory will be freed before quit.
>

Yep, precisely.

Sean

>>
>>
>>>          bool mode_config_initialized;
>>>
>>>          /* ttm */
>>> @@ -85,6 +86,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct
>>> drm_gem_object *gem)
>>>   #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
>>>
>>>   int hibmc_plane_init(struct hibmc_drm_device *hidev);
>>> +int hibmc_crtc_init(struct hibmc_drm_device *hidev);
>>>   int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
>>>   void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
>>>
>>> --
>>> 1.9.1
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>> _______________________________________________
>> linuxarm mailing list
>> linuxarm@huawei.com
>> http://rnd-openeuler.huawei.com/mailman/listinfo/linuxarm
>>
>> .
>>
>
>
> --
> Regards, Rongrong
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
diff mbox

Patch

diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index 9c1a68c..9b5d0d0 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -23,6 +23,7 @@ 
 
 #include "hibmc_drm_drv.h"
 #include "hibmc_drm_regs.h"
+#include "hibmc_drm_de.h"
 #include "hibmc_drm_power.h"
 
 /* ---------------------------------------------------------------------- */
@@ -168,3 +169,320 @@  int hibmc_plane_init(struct hibmc_drm_device *hidev)
 	drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
 	return 0;
 }
+
+static void hibmc_crtc_enable(struct drm_crtc *crtc)
+{
+	unsigned int reg;
+	/* power mode 0 is default. */
+	struct hibmc_drm_device *hidev = crtc->dev->dev_private;
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
+	reg |= HIBMC_CURR_GATE_DISPLAY(ON);
+	hibmc_set_current_gate(hidev, reg);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void hibmc_crtc_disable(struct drm_crtc *crtc)
+{
+	unsigned int reg;
+	struct hibmc_drm_device *hidev = crtc->dev->dev_private;
+
+	drm_crtc_vblank_off(crtc);
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_SLEEP);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg |= HIBMC_CURR_GATE_LOCALMEM(OFF);
+	reg |= HIBMC_CURR_GATE_DISPLAY(OFF);
+	hibmc_set_current_gate(hidev, reg);
+}
+
+static int hibmc_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	return 0;
+}
+
+static unsigned int format_pll_reg(void)
+{
+	unsigned int pllreg = 0;
+	struct panel_pll pll = {0};
+
+	/* Note that all PLL's have the same format. Here,
+	 * we just use Panel PLL parameter to work out the bit
+	 * fields in the register.On returning a 32 bit number, the value can
+	 * be applied to any PLL in the calling function.
+	 */
+	pllreg |= HIBMC_PLL_CTRL_BYPASS(OFF) & HIBMC_PLL_CTRL_BYPASS_MASK;
+	pllreg |= HIBMC_PLL_CTRL_POWER(ON) & HIBMC_PLL_CTRL_POWER_MASK;
+	pllreg |= HIBMC_PLL_CTRL_INPUT(OSC) & HIBMC_PLL_CTRL_INPUT_MASK;
+	pllreg |= HIBMC_PLL_CTRL_POD(pll.POD) & HIBMC_PLL_CTRL_POD_MASK;
+	pllreg |= HIBMC_PLL_CTRL_OD(pll.OD) & HIBMC_PLL_CTRL_OD_MASK;
+	pllreg |= HIBMC_PLL_CTRL_N(pll.N) & HIBMC_PLL_CTRL_N_MASK;
+	pllreg |= HIBMC_PLL_CTRL_M(pll.M) & HIBMC_PLL_CTRL_M_MASK;
+
+	return pllreg;
+}
+
+static void set_vclock_hisilicon(struct drm_device *dev, unsigned long pll)
+{
+	unsigned long tmp0, tmp1;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	/* 1. outer_bypass_n=0 */
+	tmp0 = readl(hidev->mmio + CRT_PLL1_HS);
+	tmp0 &= 0xBFFFFFFF;
+	writel(tmp0, hidev->mmio + CRT_PLL1_HS);
+
+	/* 2. pll_pd=1?inter_bypass=1 */
+	writel(0x21000000, hidev->mmio + CRT_PLL1_HS);
+
+	/* 3. config pll */
+	writel(pll, hidev->mmio + CRT_PLL1_HS);
+
+	/* 4. delay  */
+	mdelay(1);
+
+	/* 5. pll_pd =0 */
+	tmp1 = pll & ~0x01000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+
+	/* 6. delay  */
+	mdelay(1);
+
+	/* 7. inter_bypass=0 */
+	tmp1 &= ~0x20000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+
+	/* 8. delay  */
+	mdelay(1);
+
+	/* 9. outer_bypass_n=1 */
+	tmp1 |= 0x40000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+}
+
+/* This function takes care the extra registers and bit fields required to
+ *setup a mode in board.
+ *Explanation about Display Control register:
+ *FPGA only supports 7 predefined pixel clocks, and clock select is
+ *in bit 4:0 of new register 0x802a8.
+ */
+static unsigned int display_ctrl_adjust(struct drm_device *dev,
+					struct drm_display_mode *mode,
+					unsigned int ctrl)
+{
+	unsigned long x, y;
+	unsigned long pll1; /* bit[31:0] of PLL */
+	unsigned long pll2; /* bit[63:32] of PLL */
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	x = mode->hdisplay;
+	y = mode->vdisplay;
+
+	/* Hisilicon has to set up a new register for PLL control
+	 *(CRT_PLL1_HS & CRT_PLL2_HS).
+	 */
+	if (x == 800 && y == 600) {
+		pll1 = CRT_PLL1_HS_40MHZ;
+		pll2 = CRT_PLL2_HS_40MHZ;
+	} else if (x == 1024 && y == 768) {
+		pll1 = CRT_PLL1_HS_65MHZ;
+		pll2 = CRT_PLL2_HS_65MHZ;
+	} else if (x == 1152 && y == 864) {
+		pll1 = CRT_PLL1_HS_80MHZ_1152;
+		pll2 = CRT_PLL2_HS_80MHZ;
+	} else if (x == 1280 && y == 768) {
+		pll1 = CRT_PLL1_HS_80MHZ;
+		pll2 = CRT_PLL2_HS_80MHZ;
+	} else if (x == 1280 && y == 720) {
+		pll1 = CRT_PLL1_HS_74MHZ;
+		pll2 = CRT_PLL2_HS_74MHZ;
+	} else if (x == 1280 && y == 960) {
+		pll1 = CRT_PLL1_HS_108MHZ;
+		pll2 = CRT_PLL2_HS_108MHZ;
+	} else if (x == 1280 && y == 1024) {
+		pll1 = CRT_PLL1_HS_108MHZ;
+		pll2 = CRT_PLL2_HS_108MHZ;
+	} else if (x == 1600 && y == 1200) {
+		pll1 = CRT_PLL1_HS_162MHZ;
+		pll2 = CRT_PLL2_HS_162MHZ;
+	} else if (x == 1920 && y == 1080) {
+		pll1 = CRT_PLL1_HS_148MHZ;
+		pll2 = CRT_PLL2_HS_148MHZ;
+	} else if (x == 1920 && y == 1200) {
+		pll1 = CRT_PLL1_HS_193MHZ;
+		pll2 = CRT_PLL2_HS_193MHZ;
+	} else /* default to VGA clock */ {
+		pll1 = CRT_PLL1_HS_25MHZ;
+		pll2 = CRT_PLL2_HS_25MHZ;
+	}
+
+	writel(pll2, hidev->mmio + CRT_PLL2_HS);
+	set_vclock_hisilicon(dev, pll1);
+
+	/* Hisilicon has to set up the top-left and bottom-right
+	 * registers as well.
+	 * Note that normal chip only use those two register for
+	 * auto-centering mode.
+	 */
+	writel((HIBMC_CRT_AUTO_CENTERING_TL_TOP(0) &
+		HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK) |
+	       (HIBMC_CRT_AUTO_CENTERING_TL_LEFT(0) &
+		HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK),
+	       hidev->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
+
+	writel((HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(y - 1) &
+		HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
+	       (HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x - 1) &
+		HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK),
+		hidev->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
+
+	/* Assume common fields in ctrl have been properly set before
+	 * calling this function.
+	 * This function only sets the extra fields in ctrl.
+	 */
+
+	/* Set bit 25 of display controller: Select CRT or VGA clock */
+	ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
+	ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
+
+	ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(CRTSELECT_CRT);
+
+	/*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL, CRTSELECT, CRT);*/
+
+	/* Set bit 14 of display controller */
+	/*ctrl &= FIELD_CLEAR(HIBMC_CRT_DISP_CTL, CLOCK_PHASE);*/
+
+	/* clock_phase_polarity is 0 */
+	ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(PHASE_ACTIVE_HIGH);
+	/*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL,*/
+	/*CLOCK_PHASE, ACTIVE_HIGH);*/
+
+	writel(ctrl, hidev->mmio + HIBMC_CRT_DISP_CTL);
+
+	return ctrl;
+}
+
+static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	unsigned int val;
+	struct drm_display_mode *mode = &crtc->state->mode;
+	struct drm_device *dev = crtc->dev;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	writel(format_pll_reg(), hidev->mmio + HIBMC_CRT_PLL_CTRL);
+	writel((HIBMC_CRT_HORZ_TOTAL_TOTAL(mode->htotal - 1) &
+		HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK) |
+		(HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(mode->hdisplay - 1) &
+		HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK),
+		hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
+
+	writel((HIBMC_CRT_HORZ_SYNC_WIDTH(mode->hsync_end - mode->hsync_start)
+		& HIBMC_CRT_HORZ_SYNC_WIDTH_MASK) |
+		(HIBMC_CRT_HORZ_SYNC_START(mode->hsync_start - 1)
+		& HIBMC_CRT_HORZ_SYNC_START_MASK),
+		hidev->mmio + HIBMC_CRT_HORZ_SYNC);
+
+	writel((HIBMC_CRT_VERT_TOTAL_TOTAL(mode->vtotal - 1) &
+		HIBMC_CRT_VERT_TOTAL_TOTAL_MASK) |
+		(HIBMC_CRT_VERT_TOTAL_DISPLAY_END(mode->vdisplay - 1) &
+		HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK),
+		hidev->mmio + HIBMC_CRT_VERT_TOTAL);
+
+	writel((HIBMC_CRT_VERT_SYNC_HEIGHT(mode->vsync_end - mode->vsync_start)
+		& HIBMC_CRT_VERT_SYNC_HEIGHT_MASK) |
+	       (HIBMC_CRT_VERT_SYNC_START(mode->vsync_start - 1) &
+		HIBMC_CRT_VERT_SYNC_START_MASK),
+		hidev->mmio + HIBMC_CRT_VERT_SYNC);
+
+	val = HIBMC_CRT_DISP_CTL_VSYNC_PHASE(0) &
+	      HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK;
+	val |= HIBMC_CRT_DISP_CTL_HSYNC_PHASE(0) &
+	       HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK;
+	val |= HIBMC_CRT_DISP_CTL_TIMING(ENABLE);
+	val |= HIBMC_CRT_DISP_CTL_PLANE(ENABLE);
+
+	display_ctrl_adjust(dev, mode, val);
+}
+
+static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+{
+	unsigned int reg;
+	struct drm_device *dev = crtc->dev;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg |= HIBMC_CURR_GATE_DISPLAY(ON);
+	reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
+	hibmc_set_current_gate(hidev, reg);
+
+	/* We can add more initialization as needed. */
+}
+
+static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&crtc->dev->event_lock, flags);
+	if (crtc->state->event)
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+	crtc->state->event = NULL;
+
+	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+
+/* These provide the minimum set of functions required to handle a CRTC */
+static const struct drm_crtc_funcs hibmc_crtc_funcs = {
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = drm_crtc_cleanup,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state =  drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
+	.enable		= hibmc_crtc_enable,
+	.disable	= hibmc_crtc_disable,
+	.mode_set_nofb	= hibmc_crtc_mode_set_nofb,
+	.atomic_check	= hibmc_crtc_atomic_check,
+	.atomic_begin	= hibmc_crtc_atomic_begin,
+	.atomic_flush	= hibmc_crtc_atomic_flush,
+};
+
+int hibmc_crtc_init(struct hibmc_drm_device *hidev)
+{
+	struct drm_device *dev = hidev->dev;
+	struct drm_crtc *crtc = &hidev->crtc;
+	struct drm_plane *plane = &hidev->plane;
+	int ret;
+
+	ret = drm_crtc_init_with_planes(dev, crtc, plane,
+					NULL, &hibmc_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
+	drm_mode_crtc_set_gamma_size(crtc, 256);
+	drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
+	return 0;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 7d96583..303cd36 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -119,6 +119,12 @@  static int hibmc_kms_init(struct hibmc_drm_device *hidev)
 		return ret;
 	}
 
+	ret = hibmc_crtc_init(hidev);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 49e39d2..5731ec2 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -46,6 +46,7 @@  struct hibmc_drm_device {
 	/* drm */
 	struct drm_device  *dev;
 	struct drm_plane plane;
+	struct drm_crtc crtc;
 	bool mode_config_initialized;
 
 	/* ttm */
@@ -85,6 +86,7 @@  static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 
 int hibmc_plane_init(struct hibmc_drm_device *hidev);
+int hibmc_crtc_init(struct hibmc_drm_device *hidev);
 int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
 void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);