Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
0004  * Author: Brian Starkey <brian.starkey@arm.com>
0005  *
0006  * ARM Mali DP Writeback connector implementation
0007  */
0008 
0009 #include <drm/drm_atomic.h>
0010 #include <drm/drm_atomic_helper.h>
0011 #include <drm/drm_crtc.h>
0012 #include <drm/drm_edid.h>
0013 #include <drm/drm_fb_cma_helper.h>
0014 #include <drm/drm_fourcc.h>
0015 #include <drm/drm_framebuffer.h>
0016 #include <drm/drm_gem_cma_helper.h>
0017 #include <drm/drm_probe_helper.h>
0018 #include <drm/drm_writeback.h>
0019 
0020 #include "malidp_drv.h"
0021 #include "malidp_hw.h"
0022 #include "malidp_mw.h"
0023 
0024 #define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
0025 
0026 struct malidp_mw_connector_state {
0027     struct drm_connector_state base;
0028     dma_addr_t addrs[2];
0029     s32 pitches[2];
0030     u8 format;
0031     u8 n_planes;
0032     bool rgb2yuv_initialized;
0033     const s16 *rgb2yuv_coeffs;
0034 };
0035 
0036 static int malidp_mw_connector_get_modes(struct drm_connector *connector)
0037 {
0038     struct drm_device *dev = connector->dev;
0039 
0040     return drm_add_modes_noedid(connector, dev->mode_config.max_width,
0041                     dev->mode_config.max_height);
0042 }
0043 
0044 static enum drm_mode_status
0045 malidp_mw_connector_mode_valid(struct drm_connector *connector,
0046                    struct drm_display_mode *mode)
0047 {
0048     struct drm_device *dev = connector->dev;
0049     struct drm_mode_config *mode_config = &dev->mode_config;
0050     int w = mode->hdisplay, h = mode->vdisplay;
0051 
0052     if ((w < mode_config->min_width) || (w > mode_config->max_width))
0053         return MODE_BAD_HVALUE;
0054 
0055     if ((h < mode_config->min_height) || (h > mode_config->max_height))
0056         return MODE_BAD_VVALUE;
0057 
0058     return MODE_OK;
0059 }
0060 
0061 static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
0062     .get_modes = malidp_mw_connector_get_modes,
0063     .mode_valid = malidp_mw_connector_mode_valid,
0064 };
0065 
0066 static void malidp_mw_connector_reset(struct drm_connector *connector)
0067 {
0068     struct malidp_mw_connector_state *mw_state =
0069         kzalloc(sizeof(*mw_state), GFP_KERNEL);
0070 
0071     if (connector->state)
0072         __drm_atomic_helper_connector_destroy_state(connector->state);
0073 
0074     kfree(connector->state);
0075     __drm_atomic_helper_connector_reset(connector, &mw_state->base);
0076 }
0077 
0078 static enum drm_connector_status
0079 malidp_mw_connector_detect(struct drm_connector *connector, bool force)
0080 {
0081     return connector_status_connected;
0082 }
0083 
0084 static void malidp_mw_connector_destroy(struct drm_connector *connector)
0085 {
0086     drm_connector_cleanup(connector);
0087 }
0088 
0089 static struct drm_connector_state *
0090 malidp_mw_connector_duplicate_state(struct drm_connector *connector)
0091 {
0092     struct malidp_mw_connector_state *mw_state, *mw_current_state;
0093 
0094     if (WARN_ON(!connector->state))
0095         return NULL;
0096 
0097     mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL);
0098     if (!mw_state)
0099         return NULL;
0100 
0101     mw_current_state = to_mw_state(connector->state);
0102     mw_state->rgb2yuv_coeffs = mw_current_state->rgb2yuv_coeffs;
0103     mw_state->rgb2yuv_initialized = mw_current_state->rgb2yuv_initialized;
0104 
0105     __drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base);
0106 
0107     return &mw_state->base;
0108 }
0109 
0110 static const struct drm_connector_funcs malidp_mw_connector_funcs = {
0111     .reset = malidp_mw_connector_reset,
0112     .detect = malidp_mw_connector_detect,
0113     .fill_modes = drm_helper_probe_single_connector_modes,
0114     .destroy = malidp_mw_connector_destroy,
0115     .atomic_duplicate_state = malidp_mw_connector_duplicate_state,
0116     .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
0117 };
0118 
0119 static const s16 rgb2yuv_coeffs_bt709_limited[MALIDP_COLORADJ_NUM_COEFFS] = {
0120     47,  157,   16,
0121     -26,  -87,  112,
0122     112, -102,  -10,
0123     16,  128,  128
0124 };
0125 
0126 static int
0127 malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
0128                    struct drm_crtc_state *crtc_state,
0129                    struct drm_connector_state *conn_state)
0130 {
0131     struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state);
0132     struct malidp_drm *malidp = encoder->dev->dev_private;
0133     struct drm_framebuffer *fb;
0134     int i, n_planes;
0135 
0136     if (!conn_state->writeback_job)
0137         return 0;
0138 
0139     fb = conn_state->writeback_job->fb;
0140     if ((fb->width != crtc_state->mode.hdisplay) ||
0141         (fb->height != crtc_state->mode.vdisplay)) {
0142         DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
0143                 fb->width, fb->height);
0144         return -EINVAL;
0145     }
0146 
0147     if (fb->modifier) {
0148         DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n");
0149         return -EINVAL;
0150     }
0151 
0152     mw_state->format =
0153         malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
0154                     fb->format->format, !!fb->modifier);
0155     if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
0156         DRM_DEBUG_KMS("Invalid pixel format %p4cc\n",
0157                   &fb->format->format);
0158         return -EINVAL;
0159     }
0160 
0161     n_planes = fb->format->num_planes;
0162     for (i = 0; i < n_planes; i++) {
0163         struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i);
0164         /* memory write buffers are never rotated */
0165         u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0);
0166 
0167         if (fb->pitches[i] & (alignment - 1)) {
0168             DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
0169                       fb->pitches[i], i);
0170             return -EINVAL;
0171         }
0172         mw_state->pitches[i] = fb->pitches[i];
0173         mw_state->addrs[i] = obj->paddr + fb->offsets[i];
0174     }
0175     mw_state->n_planes = n_planes;
0176 
0177     if (fb->format->is_yuv)
0178         mw_state->rgb2yuv_coeffs = rgb2yuv_coeffs_bt709_limited;
0179 
0180     return 0;
0181 }
0182 
0183 static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
0184     .atomic_check = malidp_mw_encoder_atomic_check,
0185 };
0186 
0187 static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats)
0188 {
0189     const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
0190     u32 *formats;
0191     int n, i;
0192 
0193     formats = kcalloc(map->n_pixel_formats, sizeof(*formats),
0194               GFP_KERNEL);
0195     if (!formats)
0196         return NULL;
0197 
0198     for (n = 0, i = 0;  i < map->n_pixel_formats; i++) {
0199         if (map->pixel_formats[i].layer & SE_MEMWRITE)
0200             formats[n++] = map->pixel_formats[i].format;
0201     }
0202 
0203     *n_formats = n;
0204 
0205     return formats;
0206 }
0207 
0208 int malidp_mw_connector_init(struct drm_device *drm)
0209 {
0210     struct malidp_drm *malidp = drm->dev_private;
0211     u32 *formats;
0212     int ret, n_formats;
0213 
0214     if (!malidp->dev->hw->enable_memwrite)
0215         return 0;
0216 
0217     drm_connector_helper_add(&malidp->mw_connector.base,
0218                  &malidp_mw_connector_helper_funcs);
0219 
0220     formats = get_writeback_formats(malidp, &n_formats);
0221     if (!formats)
0222         return -ENOMEM;
0223 
0224     ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
0225                        &malidp_mw_connector_funcs,
0226                        &malidp_mw_encoder_helper_funcs,
0227                        formats, n_formats,
0228                        1 << drm_crtc_index(&malidp->crtc));
0229     kfree(formats);
0230     if (ret)
0231         return ret;
0232 
0233     return 0;
0234 }
0235 
0236 void malidp_mw_atomic_commit(struct drm_device *drm,
0237                  struct drm_atomic_state *old_state)
0238 {
0239     struct malidp_drm *malidp = drm->dev_private;
0240     struct drm_writeback_connector *mw_conn = &malidp->mw_connector;
0241     struct drm_connector_state *conn_state = mw_conn->base.state;
0242     struct malidp_hw_device *hwdev = malidp->dev;
0243     struct malidp_mw_connector_state *mw_state;
0244 
0245     if (!conn_state)
0246         return;
0247 
0248     mw_state = to_mw_state(conn_state);
0249 
0250     if (conn_state->writeback_job) {
0251         struct drm_framebuffer *fb = conn_state->writeback_job->fb;
0252 
0253         DRM_DEV_DEBUG_DRIVER(drm->dev,
0254                      "Enable memwrite %ux%u:%d %pad fmt: %u\n",
0255                      fb->width, fb->height,
0256                      mw_state->pitches[0],
0257                      &mw_state->addrs[0],
0258                      mw_state->format);
0259 
0260         drm_writeback_queue_job(mw_conn, conn_state);
0261         hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
0262                        mw_state->pitches, mw_state->n_planes,
0263                        fb->width, fb->height, mw_state->format,
0264                        !mw_state->rgb2yuv_initialized ?
0265                        mw_state->rgb2yuv_coeffs : NULL);
0266         mw_state->rgb2yuv_initialized = !!mw_state->rgb2yuv_coeffs;
0267     } else {
0268         DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n");
0269         hwdev->hw->disable_memwrite(hwdev);
0270     }
0271 }