1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_device.h>
10 #include <drm/drm_fb_cma_helper.h>
11 #include <drm/drm_gem_cma_helper.h>
12 #include <drm/drm_plane_helper.h>
13 #include <drm/drm_probe_helper.h>
14 #include <linux/clk.h>
15 #include <linux/platform_data/simplefb.h>
18 #include "arcpgu_regs.h"
20 #define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1))
22 static const u32 arc_pgu_supported_formats
[] = {
28 static void arc_pgu_set_pxl_fmt(struct drm_crtc
*crtc
)
30 struct arcpgu_drm_private
*arcpgu
= crtc_to_arcpgu_priv(crtc
);
31 const struct drm_framebuffer
*fb
= crtc
->primary
->state
->fb
;
32 uint32_t pixel_format
= fb
->format
->format
;
33 u32 format
= DRM_FORMAT_INVALID
;
37 for (i
= 0; i
< ARRAY_SIZE(arc_pgu_supported_formats
); i
++) {
38 if (arc_pgu_supported_formats
[i
] == pixel_format
)
39 format
= arc_pgu_supported_formats
[i
];
42 if (WARN_ON(format
== DRM_FORMAT_INVALID
))
45 reg_ctrl
= arc_pgu_read(arcpgu
, ARCPGU_REG_CTRL
);
46 if (format
== DRM_FORMAT_RGB565
)
47 reg_ctrl
&= ~ARCPGU_MODE_XRGB8888
;
49 reg_ctrl
|= ARCPGU_MODE_XRGB8888
;
50 arc_pgu_write(arcpgu
, ARCPGU_REG_CTRL
, reg_ctrl
);
53 static const struct drm_crtc_funcs arc_pgu_crtc_funcs
= {
54 .destroy
= drm_crtc_cleanup
,
55 .set_config
= drm_atomic_helper_set_config
,
56 .page_flip
= drm_atomic_helper_page_flip
,
57 .reset
= drm_atomic_helper_crtc_reset
,
58 .atomic_duplicate_state
= drm_atomic_helper_crtc_duplicate_state
,
59 .atomic_destroy_state
= drm_atomic_helper_crtc_destroy_state
,
62 static enum drm_mode_status
arc_pgu_crtc_mode_valid(struct drm_crtc
*crtc
,
63 const struct drm_display_mode
*mode
)
65 struct arcpgu_drm_private
*arcpgu
= crtc_to_arcpgu_priv(crtc
);
66 long rate
, clk_rate
= mode
->clock
* 1000;
67 long diff
= clk_rate
/ 200; /* +-0.5% allowed by HDMI spec */
69 rate
= clk_round_rate(arcpgu
->clk
, clk_rate
);
70 if ((max(rate
, clk_rate
) - min(rate
, clk_rate
) < diff
) && (rate
> 0))
76 static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc
*crtc
)
78 struct arcpgu_drm_private
*arcpgu
= crtc_to_arcpgu_priv(crtc
);
79 struct drm_display_mode
*m
= &crtc
->state
->adjusted_mode
;
82 arc_pgu_write(arcpgu
, ARCPGU_REG_FMT
,
83 ENCODE_PGU_XY(m
->crtc_htotal
, m
->crtc_vtotal
));
85 arc_pgu_write(arcpgu
, ARCPGU_REG_HSYNC
,
86 ENCODE_PGU_XY(m
->crtc_hsync_start
- m
->crtc_hdisplay
,
87 m
->crtc_hsync_end
- m
->crtc_hdisplay
));
89 arc_pgu_write(arcpgu
, ARCPGU_REG_VSYNC
,
90 ENCODE_PGU_XY(m
->crtc_vsync_start
- m
->crtc_vdisplay
,
91 m
->crtc_vsync_end
- m
->crtc_vdisplay
));
93 arc_pgu_write(arcpgu
, ARCPGU_REG_ACTIVE
,
94 ENCODE_PGU_XY(m
->crtc_hblank_end
- m
->crtc_hblank_start
,
95 m
->crtc_vblank_end
- m
->crtc_vblank_start
));
97 val
= arc_pgu_read(arcpgu
, ARCPGU_REG_CTRL
);
99 if (m
->flags
& DRM_MODE_FLAG_PVSYNC
)
100 val
|= ARCPGU_CTRL_VS_POL_MASK
<< ARCPGU_CTRL_VS_POL_OFST
;
102 val
&= ~(ARCPGU_CTRL_VS_POL_MASK
<< ARCPGU_CTRL_VS_POL_OFST
);
104 if (m
->flags
& DRM_MODE_FLAG_PHSYNC
)
105 val
|= ARCPGU_CTRL_HS_POL_MASK
<< ARCPGU_CTRL_HS_POL_OFST
;
107 val
&= ~(ARCPGU_CTRL_HS_POL_MASK
<< ARCPGU_CTRL_HS_POL_OFST
);
109 arc_pgu_write(arcpgu
, ARCPGU_REG_CTRL
, val
);
110 arc_pgu_write(arcpgu
, ARCPGU_REG_STRIDE
, 0);
111 arc_pgu_write(arcpgu
, ARCPGU_REG_START_SET
, 1);
113 arc_pgu_set_pxl_fmt(crtc
);
115 clk_set_rate(arcpgu
->clk
, m
->crtc_clock
* 1000);
118 static void arc_pgu_crtc_atomic_enable(struct drm_crtc
*crtc
,
119 struct drm_atomic_state
*state
)
121 struct arcpgu_drm_private
*arcpgu
= crtc_to_arcpgu_priv(crtc
);
123 clk_prepare_enable(arcpgu
->clk
);
124 arc_pgu_write(arcpgu
, ARCPGU_REG_CTRL
,
125 arc_pgu_read(arcpgu
, ARCPGU_REG_CTRL
) |
126 ARCPGU_CTRL_ENABLE_MASK
);
129 static void arc_pgu_crtc_atomic_disable(struct drm_crtc
*crtc
,
130 struct drm_atomic_state
*state
)
132 struct arcpgu_drm_private
*arcpgu
= crtc_to_arcpgu_priv(crtc
);
134 clk_disable_unprepare(arcpgu
->clk
);
135 arc_pgu_write(arcpgu
, ARCPGU_REG_CTRL
,
136 arc_pgu_read(arcpgu
, ARCPGU_REG_CTRL
) &
137 ~ARCPGU_CTRL_ENABLE_MASK
);
140 static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs
= {
141 .mode_valid
= arc_pgu_crtc_mode_valid
,
142 .mode_set_nofb
= arc_pgu_crtc_mode_set_nofb
,
143 .atomic_enable
= arc_pgu_crtc_atomic_enable
,
144 .atomic_disable
= arc_pgu_crtc_atomic_disable
,
147 static void arc_pgu_plane_atomic_update(struct drm_plane
*plane
,
148 struct drm_plane_state
*state
)
150 struct arcpgu_drm_private
*arcpgu
;
151 struct drm_gem_cma_object
*gem
;
153 if (!plane
->state
->crtc
|| !plane
->state
->fb
)
156 arcpgu
= crtc_to_arcpgu_priv(plane
->state
->crtc
);
157 gem
= drm_fb_cma_get_gem_obj(plane
->state
->fb
, 0);
158 arc_pgu_write(arcpgu
, ARCPGU_REG_BUF0_ADDR
, gem
->paddr
);
161 static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs
= {
162 .atomic_update
= arc_pgu_plane_atomic_update
,
165 static void arc_pgu_plane_destroy(struct drm_plane
*plane
)
167 drm_plane_cleanup(plane
);
170 static const struct drm_plane_funcs arc_pgu_plane_funcs
= {
171 .update_plane
= drm_atomic_helper_update_plane
,
172 .disable_plane
= drm_atomic_helper_disable_plane
,
173 .destroy
= arc_pgu_plane_destroy
,
174 .reset
= drm_atomic_helper_plane_reset
,
175 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
176 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
179 static struct drm_plane
*arc_pgu_plane_init(struct drm_device
*drm
)
181 struct arcpgu_drm_private
*arcpgu
= drm
->dev_private
;
182 struct drm_plane
*plane
= NULL
;
185 plane
= devm_kzalloc(drm
->dev
, sizeof(*plane
), GFP_KERNEL
);
187 return ERR_PTR(-ENOMEM
);
189 ret
= drm_universal_plane_init(drm
, plane
, 0xff, &arc_pgu_plane_funcs
,
190 arc_pgu_supported_formats
,
191 ARRAY_SIZE(arc_pgu_supported_formats
),
193 DRM_PLANE_TYPE_PRIMARY
, NULL
);
197 drm_plane_helper_add(plane
, &arc_pgu_plane_helper_funcs
);
198 arcpgu
->plane
= plane
;
203 int arc_pgu_setup_crtc(struct drm_device
*drm
)
205 struct arcpgu_drm_private
*arcpgu
= drm
->dev_private
;
206 struct drm_plane
*primary
;
209 primary
= arc_pgu_plane_init(drm
);
211 return PTR_ERR(primary
);
213 ret
= drm_crtc_init_with_planes(drm
, &arcpgu
->crtc
, primary
, NULL
,
214 &arc_pgu_crtc_funcs
, NULL
);
216 arc_pgu_plane_destroy(primary
);
220 drm_crtc_helper_add(&arcpgu
->crtc
, &arc_pgu_crtc_helper_funcs
);