Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2017 Free Electrons
0004  * Maxime Ripard <maxime.ripard@free-electrons.com>
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  * These coefficients are taken from the A33 BSP from Allwinner.
0058  *
0059  * The first three values of each row are coded as 13-bit signed fixed-point
0060  * numbers, with 10 bits for the fractional part. The fourth value is a
0061  * constant coded as a 14-bit signed fixed-point number with 4 bits for the
0062  * fractional part.
0063  *
0064  * The values in table order give the following colorspace translation:
0065  * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
0066  * R = 1.164 * Y + 1.596 * V - 222
0067  * B = 1.164 * Y + 2.018 * U + 276
0068  *
0069  * This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255],
0070  * following the BT601 spec.
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          * The X1 offset is the offset to the bottom-right point in the
0174          * end tile, which is the final pixel (at offset width - 1)
0175          * within the end tile (with a 32-byte mask).
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     /* Set the line width */
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     /* Some planar formats require chroma channel swapping by hand. */
0221     swap = sun4i_frontend_format_chroma_requires_swap(fb->format->format);
0222 
0223     /* Set the physical address of the buffer in memory */
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     /* Planar formats have an explicit input sequence. */
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      * I have no idea what this does exactly, but it seems to be
0441      * related to the scaler FIR filter phase parameters.
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      * Checking the input format is sufficient since we currently only
0459      * support RGB output formats to the backend. If YUV output formats
0460      * ever get supported, an YUV input and output would require bypassing
0461      * the CSC engine too.
0462      */
0463     if (format->is_yuv) {
0464         /* Setup the CSC engine for YUV to RGB conversion. */
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      * TODO: It look like the A31 and A80 at least will need the
0483      * bit 7 (ALPHA_EN) enabled when using a format with alpha (so
0484      * ARGB8888).
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     /* Set height and width */
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");