linux/drivers/gpu/drm/msm/msm_fb.c

252 lines
6.6 KiB
C
Raw Normal View History

/*
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h"
struct msm_framebuffer {
struct drm_framebuffer base;
const struct msm_format *format;
};
#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
.create_handle = drm_gem_fb_create_handle,
.destroy = drm_gem_fb_destroy,
};
#ifdef CONFIG_DEBUG_FS
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
{
int i, n = fb->format->num_planes;
seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
drm: Nuke fb->pixel_format Replace uses of fb->pixel_format with fb->format->format. Less duplicated information is a good thing. Note that coccinelle failed to eliminate the "/* fourcc format */" comment from drm_framebuffer.h, so I had to do that part manually. @@ struct drm_framebuffer *FB; expression E; @@ drm_helper_mode_fill_fb_struct(...) { ... - FB->pixel_format = E; ... } @@ struct drm_framebuffer *FB; expression E; @@ i9xx_get_initial_plane_config(...) { ... - FB->pixel_format = E; ... } @@ struct drm_framebuffer *FB; expression E; @@ ironlake_get_initial_plane_config(...) { ... - FB->pixel_format = E; ... } @@ struct drm_framebuffer *FB; expression E; @@ skylake_get_initial_plane_config(...) { ... - FB->pixel_format = E; ... } @@ struct drm_framebuffer *a; struct drm_framebuffer b; @@ ( - a->pixel_format + a->format->format | - b.pixel_format + b.format->format ) @@ struct drm_plane_state *a; struct drm_plane_state b; @@ ( - a->fb->pixel_format + a->fb->format->format | - b.fb->pixel_format + b.fb->format->format ) @@ struct drm_crtc *CRTC; @@ ( - CRTC->primary->fb->pixel_format + CRTC->primary->fb->format->format | - CRTC->primary->state->fb->pixel_format + CRTC->primary->state->fb->format->format ) @@ struct drm_mode_set *set; @@ ( - set->fb->pixel_format + set->fb->format->format | - set->crtc->primary->fb->pixel_format + set->crtc->primary->fb->format->format ) @@ @@ struct drm_framebuffer { ... - uint32_t pixel_format; ... }; v2: Fix commit message (Laurent) Rebase due to earlier removal of many fb->pixel_format uses, including the 'fb->format = drm_format_info(fb->format->format);' snafu v3: Adjusted the semantic patch a bit and regenerated due to code changes Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> (v1) Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: http://patchwork.freedesktop.org/patch/msgid/1481751175-18463-1-git-send-email-ville.syrjala@linux.intel.com
2016-12-15 05:32:55 +08:00
fb->width, fb->height, (char *)&fb->format->format,
drm_framebuffer_read_refcount(fb), fb->base.id);
for (i = 0; i < n; i++) {
seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
i, fb->offsets[i], fb->pitches[i]);
msm_gem_describe(fb->obj[i], m);
}
}
#endif
/* prepare/pin all the fb's bo's for scanout. Note that it is not valid
* to prepare an fb more multiple different initiator 'id's. But that
* should be fine, since only the scanout (mdpN) side of things needs
* this, the gpu doesn't care about fb's.
*/
int msm_framebuffer_prepare(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace)
{
int ret, i, n = fb->format->num_planes;
uint64_t iova;
for (i = 0; i < n; i++) {
ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
if (ret)
return ret;
}
return 0;
}
void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace)
{
int i, n = fb->format->num_planes;
for (i = 0; i < n; i++)
msm_gem_put_iova(fb->obj[i], aspace);
}
uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace, int plane)
{
if (!fb->obj[plane])
return 0;
return msm_gem_iova(fb->obj[plane], aspace) + fb->offsets[plane];
}
struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
{
return drm_gem_fb_get_obj(fb, plane);
}
const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
{
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
return msm_fb->format;
}
struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_gem_object *bos[4] = {0};
struct drm_framebuffer *fb;
int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
for (i = 0; i < n; i++) {
bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
if (!bos[i]) {
ret = -ENXIO;
goto out_unref;
}
}
fb = msm_framebuffer_init(dev, mode_cmd, bos);
if (IS_ERR(fb)) {
ret = PTR_ERR(fb);
goto out_unref;
}
return fb;
out_unref:
for (i = 0; i < n; i++)
drm_gem_object_put_unlocked(bos[i]);
return ERR_PTR(ret);
}
static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
{
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
drm/msm: fix unbalanced DRM framebuffer init/destroy When msm_framebuffer_init() fails before calling drm_framebuffer_init(), drm_framebuffer_cleanup() [called in msm_framebuffer_destroy()] is still being called even though drm_framebuffer_init() was not called for that buffer. Thus a NULL pointer derefencing: [ 247.529691] Unable to handle kernel NULL pointer dereference at virtual address 0000027c ... [ 247.563996] PC is at __mutex_lock_slowpath+0x94/0x3a8 ... [ 247.823025] [<c07c3c78>] (__mutex_lock_slowpath) from [<c07c3fac>] (mutex_lock+0x20/0x3c) [ 247.831186] [<c07c3fac>] (mutex_lock) from [<c0347cf0>] (drm_framebuffer_cleanup+0x18/0x38) [ 247.839520] [<c0347cf0>] (drm_framebuffer_cleanup) from [<c036d138>] (msm_framebuffer_destroy+0x48/0x100) [ 247.849066] [<c036d138>] (msm_framebuffer_destroy) from [<c036d580>] (msm_framebuffer_init+0x1e8/0x228) [ 247.858439] [<c036d580>] (msm_framebuffer_init) from [<c036d630>] (msm_framebuffer_create+0x70/0x134) [ 247.867642] [<c036d630>] (msm_framebuffer_create) from [<c03493ec>] (internal_framebuffer_create+0x67c/0x7b4) [ 247.877537] [<c03493ec>] (internal_framebuffer_create) from [<c034ce34>] (drm_mode_addfb2+0x20/0x98) [ 247.886650] [<c034ce34>] (drm_mode_addfb2) from [<c034071c>] (drm_ioctl+0x240/0x420) [ 247.894378] [<c034071c>] (drm_ioctl) from [<c011df7c>] (do_vfs_ioctl+0x4e4/0x5a4) ... Signed-off-by: Stephane Viau <sviau@codeaurora.org> [plus initialize msm_fb to NULL to -Rob] Signed-off-by: Rob Clark <robdclark@gmail.com>
2015-05-05 21:47:57 +08:00
struct msm_framebuffer *msm_fb = NULL;
struct drm_framebuffer *fb;
const struct msm_format *format;
int ret, i, n;
unsigned int hsub, vsub;
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height,
(char *)&mode_cmd->pixel_format);
n = drm_format_num_planes(mode_cmd->pixel_format);
hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
mode_cmd->modifier[0]);
if (!format) {
DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
(char *)&mode_cmd->pixel_format);
ret = -EINVAL;
goto fail;
}
msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
if (!msm_fb) {
ret = -ENOMEM;
goto fail;
}
fb = &msm_fb->base;
msm_fb->format = format;
if (n > ARRAY_SIZE(fb->obj)) {
ret = -EINVAL;
goto fail;
}
for (i = 0; i < n; i++) {
unsigned int width = mode_cmd->width / (i ? hsub : 1);
unsigned int height = mode_cmd->height / (i ? vsub : 1);
unsigned int min_size;
min_size = (height - 1) * mode_cmd->pitches[i]
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
+ mode_cmd->offsets[i];
if (bos[i]->size < min_size) {
ret = -EINVAL;
goto fail;
}
msm_fb->base.obj[i] = bos[i];
}
drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
if (ret) {
DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
goto fail;
}
DBG("create: FB ID: %d (%p)", fb->base.id, fb);
return fb;
fail:
drm/msm: fix unbalanced DRM framebuffer init/destroy When msm_framebuffer_init() fails before calling drm_framebuffer_init(), drm_framebuffer_cleanup() [called in msm_framebuffer_destroy()] is still being called even though drm_framebuffer_init() was not called for that buffer. Thus a NULL pointer derefencing: [ 247.529691] Unable to handle kernel NULL pointer dereference at virtual address 0000027c ... [ 247.563996] PC is at __mutex_lock_slowpath+0x94/0x3a8 ... [ 247.823025] [<c07c3c78>] (__mutex_lock_slowpath) from [<c07c3fac>] (mutex_lock+0x20/0x3c) [ 247.831186] [<c07c3fac>] (mutex_lock) from [<c0347cf0>] (drm_framebuffer_cleanup+0x18/0x38) [ 247.839520] [<c0347cf0>] (drm_framebuffer_cleanup) from [<c036d138>] (msm_framebuffer_destroy+0x48/0x100) [ 247.849066] [<c036d138>] (msm_framebuffer_destroy) from [<c036d580>] (msm_framebuffer_init+0x1e8/0x228) [ 247.858439] [<c036d580>] (msm_framebuffer_init) from [<c036d630>] (msm_framebuffer_create+0x70/0x134) [ 247.867642] [<c036d630>] (msm_framebuffer_create) from [<c03493ec>] (internal_framebuffer_create+0x67c/0x7b4) [ 247.877537] [<c03493ec>] (internal_framebuffer_create) from [<c034ce34>] (drm_mode_addfb2+0x20/0x98) [ 247.886650] [<c034ce34>] (drm_mode_addfb2) from [<c034071c>] (drm_ioctl+0x240/0x420) [ 247.894378] [<c034071c>] (drm_ioctl) from [<c011df7c>] (do_vfs_ioctl+0x4e4/0x5a4) ... Signed-off-by: Stephane Viau <sviau@codeaurora.org> [plus initialize msm_fb to NULL to -Rob] Signed-off-by: Rob Clark <robdclark@gmail.com>
2015-05-05 21:47:57 +08:00
kfree(msm_fb);
return ERR_PTR(ret);
}
struct drm_framebuffer *
msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
{
struct drm_mode_fb_cmd2 mode_cmd = {
.pixel_format = format,
.width = w,
.height = h,
.pitches = { p },
};
struct drm_gem_object *bo;
struct drm_framebuffer *fb;
int size;
/* allocate backing bo */
size = mode_cmd.pitches[0] * mode_cmd.height;
DBG("allocating %d bytes for fb %d", size, dev->primary->index);
bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
if (IS_ERR(bo)) {
dev_warn(dev->dev, "could not allocate stolen bo\n");
/* try regular bo: */
bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
}
if (IS_ERR(bo)) {
DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
return ERR_CAST(bo);
}
fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
if (IS_ERR(fb)) {
DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
/* note: if fb creation failed, we can't rely on fb destroy
* to unref the bo:
*/
drm_gem_object_put_unlocked(bo);
return ERR_CAST(fb);
}
return fb;
}