0001
0002
0003
0004
0005
0006
0007 #include <linux/clk.h>
0008 #include <linux/component.h>
0009 #include <linux/module.h>
0010 #include <linux/of_device.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/pm_runtime.h>
0013 #include <linux/regmap.h>
0014 #include <linux/reset.h>
0015
0016 #include <drm/drm_device.h>
0017 #include <drm/drm_fb_cma_helper.h>
0018 #include <drm/drm_fourcc.h>
0019 #include <drm/drm_framebuffer.h>
0020 #include <drm/drm_gem_cma_helper.h>
0021 #include <drm/drm_plane.h>
0022
0023 #include "sun4i_drv.h"
0024 #include "sun4i_frontend.h"
0025
0026 static const u32 sun4i_frontend_vert_coef[32] = {
0027 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
0028 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
0029 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
0030 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
0031 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
0032 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
0033 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
0034 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
0035 };
0036
0037 static const u32 sun4i_frontend_horz_coef[64] = {
0038 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
0039 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
0040 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
0041 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
0042 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
0043 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
0044 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
0045 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
0046 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
0047 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
0048 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
0049 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
0050 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
0051 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
0052 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
0053 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
0054 };
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072 const u32 sunxi_bt601_yuv2rgb_coef[12] = {
0073 0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
0074 0x000004a7, 0x00000000, 0x00000662, 0x00003211,
0075 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
0076 };
0077 EXPORT_SYMBOL(sunxi_bt601_yuv2rgb_coef);
0078
0079 static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
0080 {
0081 int i;
0082
0083 if (frontend->data->has_coef_access_ctrl)
0084 regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
0085 SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
0086 SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
0087
0088 for (i = 0; i < 32; i++) {
0089 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
0090 sun4i_frontend_horz_coef[2 * i]);
0091 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i),
0092 sun4i_frontend_horz_coef[2 * i]);
0093 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i),
0094 sun4i_frontend_horz_coef[2 * i + 1]);
0095 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i),
0096 sun4i_frontend_horz_coef[2 * i + 1]);
0097 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i),
0098 sun4i_frontend_vert_coef[i]);
0099 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i),
0100 sun4i_frontend_vert_coef[i]);
0101 }
0102
0103 if (frontend->data->has_coef_rdy)
0104 regmap_write_bits(frontend->regs,
0105 SUN4I_FRONTEND_FRM_CTRL_REG,
0106 SUN4I_FRONTEND_FRM_CTRL_COEF_RDY,
0107 SUN4I_FRONTEND_FRM_CTRL_COEF_RDY);
0108 }
0109
0110 int sun4i_frontend_init(struct sun4i_frontend *frontend)
0111 {
0112 return pm_runtime_get_sync(frontend->dev);
0113 }
0114 EXPORT_SYMBOL(sun4i_frontend_init);
0115
0116 void sun4i_frontend_exit(struct sun4i_frontend *frontend)
0117 {
0118 pm_runtime_put(frontend->dev);
0119 }
0120 EXPORT_SYMBOL(sun4i_frontend_exit);
0121
0122 static bool sun4i_frontend_format_chroma_requires_swap(uint32_t fmt)
0123 {
0124 switch (fmt) {
0125 case DRM_FORMAT_YVU411:
0126 case DRM_FORMAT_YVU420:
0127 case DRM_FORMAT_YVU422:
0128 case DRM_FORMAT_YVU444:
0129 return true;
0130
0131 default:
0132 return false;
0133 }
0134 }
0135
0136 static bool sun4i_frontend_format_supports_tiling(uint32_t fmt)
0137 {
0138 switch (fmt) {
0139 case DRM_FORMAT_NV12:
0140 case DRM_FORMAT_NV16:
0141 case DRM_FORMAT_NV21:
0142 case DRM_FORMAT_NV61:
0143 case DRM_FORMAT_YUV411:
0144 case DRM_FORMAT_YUV420:
0145 case DRM_FORMAT_YUV422:
0146 case DRM_FORMAT_YVU420:
0147 case DRM_FORMAT_YVU422:
0148 case DRM_FORMAT_YVU411:
0149 return true;
0150
0151 default:
0152 return false;
0153 }
0154 }
0155
0156 void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
0157 struct drm_plane *plane)
0158 {
0159 struct drm_plane_state *state = plane->state;
0160 struct drm_framebuffer *fb = state->fb;
0161 unsigned int strides[3] = {};
0162
0163 dma_addr_t paddr;
0164 bool swap;
0165
0166 if (fb->modifier == DRM_FORMAT_MOD_ALLWINNER_TILED) {
0167 unsigned int width = state->src_w >> 16;
0168 unsigned int offset;
0169
0170 strides[0] = SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[0]);
0171
0172
0173
0174
0175
0176
0177 offset = (width - 1) & (32 - 1);
0178
0179 regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF0_REG,
0180 SUN4I_FRONTEND_TB_OFF_X1(offset));
0181
0182 if (fb->format->num_planes > 1) {
0183 strides[1] =
0184 SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[1]);
0185
0186 regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF1_REG,
0187 SUN4I_FRONTEND_TB_OFF_X1(offset));
0188 }
0189
0190 if (fb->format->num_planes > 2) {
0191 strides[2] =
0192 SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[2]);
0193
0194 regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF2_REG,
0195 SUN4I_FRONTEND_TB_OFF_X1(offset));
0196 }
0197 } else {
0198 strides[0] = fb->pitches[0];
0199
0200 if (fb->format->num_planes > 1)
0201 strides[1] = fb->pitches[1];
0202
0203 if (fb->format->num_planes > 2)
0204 strides[2] = fb->pitches[2];
0205 }
0206
0207
0208 DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
0209 regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
0210 strides[0]);
0211
0212 if (fb->format->num_planes > 1)
0213 regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD1_REG,
0214 strides[1]);
0215
0216 if (fb->format->num_planes > 2)
0217 regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD2_REG,
0218 strides[2]);
0219
0220
0221 swap = sun4i_frontend_format_chroma_requires_swap(fb->format->format);
0222
0223
0224 paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
0225 DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", &paddr);
0226 regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
0227
0228 if (fb->format->num_planes > 1) {
0229 paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1);
0230 DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", &paddr);
0231 regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG,
0232 paddr);
0233 }
0234
0235 if (fb->format->num_planes > 2) {
0236 paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2);
0237 DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", &paddr);
0238 regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG,
0239 paddr);
0240 }
0241 }
0242 EXPORT_SYMBOL(sun4i_frontend_update_buffer);
0243
0244 static int
0245 sun4i_frontend_drm_format_to_input_fmt(const struct drm_format_info *format,
0246 u32 *val)
0247 {
0248 if (!format->is_yuv)
0249 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB;
0250 else if (drm_format_info_is_yuv_sampling_411(format))
0251 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411;
0252 else if (drm_format_info_is_yuv_sampling_420(format))
0253 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420;
0254 else if (drm_format_info_is_yuv_sampling_422(format))
0255 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422;
0256 else if (drm_format_info_is_yuv_sampling_444(format))
0257 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444;
0258 else
0259 return -EINVAL;
0260
0261 return 0;
0262 }
0263
0264 static int
0265 sun4i_frontend_drm_format_to_input_mode(const struct drm_format_info *format,
0266 uint64_t modifier, u32 *val)
0267 {
0268 bool tiled = (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED);
0269
0270 switch (format->num_planes) {
0271 case 1:
0272 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED;
0273 return 0;
0274
0275 case 2:
0276 *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR
0277 : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR;
0278 return 0;
0279
0280 case 3:
0281 *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR
0282 : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR;
0283 return 0;
0284
0285 default:
0286 return -EINVAL;
0287 }
0288 }
0289
0290 static int
0291 sun4i_frontend_drm_format_to_input_sequence(const struct drm_format_info *format,
0292 u32 *val)
0293 {
0294
0295 if (drm_format_info_is_yuv_planar(format)) {
0296 *val = 0;
0297 return 0;
0298 }
0299
0300 switch (format->format) {
0301 case DRM_FORMAT_BGRX8888:
0302 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX;
0303 return 0;
0304
0305 case DRM_FORMAT_NV12:
0306 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
0307 return 0;
0308
0309 case DRM_FORMAT_NV16:
0310 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
0311 return 0;
0312
0313 case DRM_FORMAT_NV21:
0314 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
0315 return 0;
0316
0317 case DRM_FORMAT_NV61:
0318 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
0319 return 0;
0320
0321 case DRM_FORMAT_UYVY:
0322 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY;
0323 return 0;
0324
0325 case DRM_FORMAT_VYUY:
0326 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY;
0327 return 0;
0328
0329 case DRM_FORMAT_XRGB8888:
0330 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB;
0331 return 0;
0332
0333 case DRM_FORMAT_YUYV:
0334 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV;
0335 return 0;
0336
0337 case DRM_FORMAT_YVYU:
0338 *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU;
0339 return 0;
0340
0341 default:
0342 return -EINVAL;
0343 }
0344 }
0345
0346 static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
0347 {
0348 switch (fmt) {
0349 case DRM_FORMAT_BGRX8888:
0350 *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888;
0351 return 0;
0352
0353 case DRM_FORMAT_XRGB8888:
0354 *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888;
0355 return 0;
0356
0357 default:
0358 return -EINVAL;
0359 }
0360 }
0361
0362 static const uint32_t sun4i_frontend_formats[] = {
0363 DRM_FORMAT_BGRX8888,
0364 DRM_FORMAT_NV12,
0365 DRM_FORMAT_NV16,
0366 DRM_FORMAT_NV21,
0367 DRM_FORMAT_NV61,
0368 DRM_FORMAT_UYVY,
0369 DRM_FORMAT_VYUY,
0370 DRM_FORMAT_XRGB8888,
0371 DRM_FORMAT_YUV411,
0372 DRM_FORMAT_YUV420,
0373 DRM_FORMAT_YUV422,
0374 DRM_FORMAT_YUV444,
0375 DRM_FORMAT_YUYV,
0376 DRM_FORMAT_YVU411,
0377 DRM_FORMAT_YVU420,
0378 DRM_FORMAT_YVU422,
0379 DRM_FORMAT_YVU444,
0380 DRM_FORMAT_YVYU,
0381 };
0382
0383 bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier)
0384 {
0385 unsigned int i;
0386
0387 if (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED)
0388 return sun4i_frontend_format_supports_tiling(fmt);
0389 else if (modifier != DRM_FORMAT_MOD_LINEAR)
0390 return false;
0391
0392 for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++)
0393 if (sun4i_frontend_formats[i] == fmt)
0394 return true;
0395
0396 return false;
0397 }
0398 EXPORT_SYMBOL(sun4i_frontend_format_is_supported);
0399
0400 int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
0401 struct drm_plane *plane, uint32_t out_fmt)
0402 {
0403 struct drm_plane_state *state = plane->state;
0404 struct drm_framebuffer *fb = state->fb;
0405 const struct drm_format_info *format = fb->format;
0406 uint64_t modifier = fb->modifier;
0407 unsigned int ch1_phase_idx;
0408 u32 out_fmt_val;
0409 u32 in_fmt_val, in_mod_val, in_ps_val;
0410 unsigned int i;
0411 u32 bypass;
0412 int ret;
0413
0414 ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val);
0415 if (ret) {
0416 DRM_DEBUG_DRIVER("Invalid input format\n");
0417 return ret;
0418 }
0419
0420 ret = sun4i_frontend_drm_format_to_input_mode(format, modifier,
0421 &in_mod_val);
0422 if (ret) {
0423 DRM_DEBUG_DRIVER("Invalid input mode\n");
0424 return ret;
0425 }
0426
0427 ret = sun4i_frontend_drm_format_to_input_sequence(format, &in_ps_val);
0428 if (ret) {
0429 DRM_DEBUG_DRIVER("Invalid pixel sequence\n");
0430 return ret;
0431 }
0432
0433 ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val);
0434 if (ret) {
0435 DRM_DEBUG_DRIVER("Invalid output format\n");
0436 return ret;
0437 }
0438
0439
0440
0441
0442
0443 ch1_phase_idx = (format->num_planes > 1) ? 1 : 0;
0444 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG,
0445 frontend->data->ch_phase[0]);
0446 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG,
0447 frontend->data->ch_phase[ch1_phase_idx]);
0448 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG,
0449 frontend->data->ch_phase[0]);
0450 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG,
0451 frontend->data->ch_phase[ch1_phase_idx]);
0452 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG,
0453 frontend->data->ch_phase[0]);
0454 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG,
0455 frontend->data->ch_phase[ch1_phase_idx]);
0456
0457
0458
0459
0460
0461
0462
0463 if (format->is_yuv) {
0464
0465 bypass = 0;
0466
0467 for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++)
0468 regmap_write(frontend->regs,
0469 SUN4I_FRONTEND_CSC_COEF_REG(i),
0470 sunxi_bt601_yuv2rgb_coef[i]);
0471 } else {
0472 bypass = SUN4I_FRONTEND_BYPASS_CSC_EN;
0473 }
0474
0475 regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
0476 SUN4I_FRONTEND_BYPASS_CSC_EN, bypass);
0477
0478 regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
0479 in_mod_val | in_fmt_val | in_ps_val);
0480
0481
0482
0483
0484
0485
0486 regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG,
0487 out_fmt_val);
0488
0489 return 0;
0490 }
0491 EXPORT_SYMBOL(sun4i_frontend_update_formats);
0492
0493 void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
0494 struct drm_plane *plane)
0495 {
0496 struct drm_plane_state *state = plane->state;
0497 struct drm_framebuffer *fb = state->fb;
0498 uint32_t luma_width, luma_height;
0499 uint32_t chroma_width, chroma_height;
0500
0501
0502 DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n",
0503 state->crtc_w, state->crtc_h);
0504
0505 luma_width = state->src_w >> 16;
0506 luma_height = state->src_h >> 16;
0507
0508 chroma_width = DIV_ROUND_UP(luma_width, fb->format->hsub);
0509 chroma_height = DIV_ROUND_UP(luma_height, fb->format->vsub);
0510
0511 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG,
0512 SUN4I_FRONTEND_INSIZE(luma_height, luma_width));
0513 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG,
0514 SUN4I_FRONTEND_INSIZE(chroma_height, chroma_width));
0515
0516 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG,
0517 SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
0518 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG,
0519 SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
0520
0521 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG,
0522 (luma_width << 16) / state->crtc_w);
0523 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG,
0524 (chroma_width << 16) / state->crtc_w);
0525
0526 regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG,
0527 (luma_height << 16) / state->crtc_h);
0528 regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG,
0529 (chroma_height << 16) / state->crtc_h);
0530
0531 regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
0532 SUN4I_FRONTEND_FRM_CTRL_REG_RDY,
0533 SUN4I_FRONTEND_FRM_CTRL_REG_RDY);
0534 }
0535 EXPORT_SYMBOL(sun4i_frontend_update_coord);
0536
0537 int sun4i_frontend_enable(struct sun4i_frontend *frontend)
0538 {
0539 regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
0540 SUN4I_FRONTEND_FRM_CTRL_FRM_START,
0541 SUN4I_FRONTEND_FRM_CTRL_FRM_START);
0542
0543 return 0;
0544 }
0545 EXPORT_SYMBOL(sun4i_frontend_enable);
0546
0547 static const struct regmap_config sun4i_frontend_regmap_config = {
0548 .reg_bits = 32,
0549 .val_bits = 32,
0550 .reg_stride = 4,
0551 .max_register = 0x0a14,
0552 };
0553
0554 static int sun4i_frontend_bind(struct device *dev, struct device *master,
0555 void *data)
0556 {
0557 struct platform_device *pdev = to_platform_device(dev);
0558 struct sun4i_frontend *frontend;
0559 struct drm_device *drm = data;
0560 struct sun4i_drv *drv = drm->dev_private;
0561 void __iomem *regs;
0562
0563 frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL);
0564 if (!frontend)
0565 return -ENOMEM;
0566
0567 dev_set_drvdata(dev, frontend);
0568 frontend->dev = dev;
0569 frontend->node = dev->of_node;
0570
0571 frontend->data = of_device_get_match_data(dev);
0572 if (!frontend->data)
0573 return -ENODEV;
0574
0575 regs = devm_platform_ioremap_resource(pdev, 0);
0576 if (IS_ERR(regs))
0577 return PTR_ERR(regs);
0578
0579 frontend->regs = devm_regmap_init_mmio(dev, regs,
0580 &sun4i_frontend_regmap_config);
0581 if (IS_ERR(frontend->regs)) {
0582 dev_err(dev, "Couldn't create the frontend regmap\n");
0583 return PTR_ERR(frontend->regs);
0584 }
0585
0586 frontend->reset = devm_reset_control_get(dev, NULL);
0587 if (IS_ERR(frontend->reset)) {
0588 dev_err(dev, "Couldn't get our reset line\n");
0589 return PTR_ERR(frontend->reset);
0590 }
0591
0592 frontend->bus_clk = devm_clk_get(dev, "ahb");
0593 if (IS_ERR(frontend->bus_clk)) {
0594 dev_err(dev, "Couldn't get our bus clock\n");
0595 return PTR_ERR(frontend->bus_clk);
0596 }
0597
0598 frontend->mod_clk = devm_clk_get(dev, "mod");
0599 if (IS_ERR(frontend->mod_clk)) {
0600 dev_err(dev, "Couldn't get our mod clock\n");
0601 return PTR_ERR(frontend->mod_clk);
0602 }
0603
0604 frontend->ram_clk = devm_clk_get(dev, "ram");
0605 if (IS_ERR(frontend->ram_clk)) {
0606 dev_err(dev, "Couldn't get our ram clock\n");
0607 return PTR_ERR(frontend->ram_clk);
0608 }
0609
0610 list_add_tail(&frontend->list, &drv->frontend_list);
0611 pm_runtime_enable(dev);
0612
0613 return 0;
0614 }
0615
0616 static void sun4i_frontend_unbind(struct device *dev, struct device *master,
0617 void *data)
0618 {
0619 struct sun4i_frontend *frontend = dev_get_drvdata(dev);
0620
0621 list_del(&frontend->list);
0622 pm_runtime_force_suspend(dev);
0623 }
0624
0625 static const struct component_ops sun4i_frontend_ops = {
0626 .bind = sun4i_frontend_bind,
0627 .unbind = sun4i_frontend_unbind,
0628 };
0629
0630 static int sun4i_frontend_probe(struct platform_device *pdev)
0631 {
0632 return component_add(&pdev->dev, &sun4i_frontend_ops);
0633 }
0634
0635 static int sun4i_frontend_remove(struct platform_device *pdev)
0636 {
0637 component_del(&pdev->dev, &sun4i_frontend_ops);
0638
0639 return 0;
0640 }
0641
0642 static int sun4i_frontend_runtime_resume(struct device *dev)
0643 {
0644 struct sun4i_frontend *frontend = dev_get_drvdata(dev);
0645 int ret;
0646
0647 clk_set_rate(frontend->mod_clk, 300000000);
0648
0649 clk_prepare_enable(frontend->bus_clk);
0650 clk_prepare_enable(frontend->mod_clk);
0651 clk_prepare_enable(frontend->ram_clk);
0652
0653 ret = reset_control_reset(frontend->reset);
0654 if (ret) {
0655 dev_err(dev, "Couldn't reset our device\n");
0656 return ret;
0657 }
0658
0659 regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG,
0660 SUN4I_FRONTEND_EN_EN,
0661 SUN4I_FRONTEND_EN_EN);
0662
0663 sun4i_frontend_scaler_init(frontend);
0664
0665 return 0;
0666 }
0667
0668 static int sun4i_frontend_runtime_suspend(struct device *dev)
0669 {
0670 struct sun4i_frontend *frontend = dev_get_drvdata(dev);
0671
0672 clk_disable_unprepare(frontend->ram_clk);
0673 clk_disable_unprepare(frontend->mod_clk);
0674 clk_disable_unprepare(frontend->bus_clk);
0675
0676 reset_control_assert(frontend->reset);
0677
0678 return 0;
0679 }
0680
0681 static const struct dev_pm_ops sun4i_frontend_pm_ops = {
0682 .runtime_resume = sun4i_frontend_runtime_resume,
0683 .runtime_suspend = sun4i_frontend_runtime_suspend,
0684 };
0685
0686 static const struct sun4i_frontend_data sun4i_a10_frontend = {
0687 .ch_phase = { 0x000, 0xfc000 },
0688 .has_coef_rdy = true,
0689 };
0690
0691 static const struct sun4i_frontend_data sun8i_a33_frontend = {
0692 .ch_phase = { 0x400, 0xfc400 },
0693 .has_coef_access_ctrl = true,
0694 };
0695
0696 const struct of_device_id sun4i_frontend_of_table[] = {
0697 {
0698 .compatible = "allwinner,sun4i-a10-display-frontend",
0699 .data = &sun4i_a10_frontend
0700 },
0701 {
0702 .compatible = "allwinner,sun7i-a20-display-frontend",
0703 .data = &sun4i_a10_frontend
0704 },
0705 {
0706 .compatible = "allwinner,sun8i-a23-display-frontend",
0707 .data = &sun8i_a33_frontend
0708 },
0709 {
0710 .compatible = "allwinner,sun8i-a33-display-frontend",
0711 .data = &sun8i_a33_frontend
0712 },
0713 { }
0714 };
0715 EXPORT_SYMBOL(sun4i_frontend_of_table);
0716 MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table);
0717
0718 static struct platform_driver sun4i_frontend_driver = {
0719 .probe = sun4i_frontend_probe,
0720 .remove = sun4i_frontend_remove,
0721 .driver = {
0722 .name = "sun4i-frontend",
0723 .of_match_table = sun4i_frontend_of_table,
0724 .pm = &sun4i_frontend_pm_ops,
0725 },
0726 };
0727 module_platform_driver(sun4i_frontend_driver);
0728
0729 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
0730 MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver");
0731 MODULE_LICENSE("GPL");