brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · d223176 Raw
221 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Russell King4 *  Written from the i915 driver.5 */6 7#include <linux/errno.h>8#include <linux/fb.h>9#include <linux/kernel.h>10#include <linux/module.h>11 12#include <drm/drm_crtc_helper.h>13#include <drm/drm_drv.h>14#include <drm/drm_fb_helper.h>15#include <drm/drm_fourcc.h>16 17#include "armada_crtc.h"18#include "armada_drm.h"19#include "armada_fb.h"20#include "armada_gem.h"21 22static void armada_fbdev_fb_destroy(struct fb_info *info)23{24	struct drm_fb_helper *fbh = info->par;25 26	drm_fb_helper_fini(fbh);27 28	fbh->fb->funcs->destroy(fbh->fb);29 30	drm_client_release(&fbh->client);31	drm_fb_helper_unprepare(fbh);32	kfree(fbh);33}34 35static const struct fb_ops armada_fb_ops = {36	.owner		= THIS_MODULE,37	FB_DEFAULT_IOMEM_OPS,38	DRM_FB_HELPER_DEFAULT_OPS,39	.fb_destroy	= armada_fbdev_fb_destroy,40};41 42static int armada_fbdev_create(struct drm_fb_helper *fbh,43	struct drm_fb_helper_surface_size *sizes)44{45	struct drm_device *dev = fbh->dev;46	struct drm_mode_fb_cmd2 mode;47	struct armada_framebuffer *dfb;48	struct armada_gem_object *obj;49	struct fb_info *info;50	int size, ret;51	void *ptr;52 53	memset(&mode, 0, sizeof(mode));54	mode.width = sizes->surface_width;55	mode.height = sizes->surface_height;56	mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);57	mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,58					sizes->surface_depth);59 60	size = mode.pitches[0] * mode.height;61	obj = armada_gem_alloc_private_object(dev, size);62	if (!obj) {63		DRM_ERROR("failed to allocate fb memory\n");64		return -ENOMEM;65	}66 67	ret = armada_gem_linear_back(dev, obj);68	if (ret) {69		drm_gem_object_put(&obj->obj);70		return ret;71	}72 73	ptr = armada_gem_map_object(dev, obj);74	if (!ptr) {75		drm_gem_object_put(&obj->obj);76		return -ENOMEM;77	}78 79	dfb = armada_framebuffer_create(dev, &mode, obj);80 81	/*82	 * A reference is now held by the framebuffer object if83	 * successful, otherwise this drops the ref for the error path.84	 */85	drm_gem_object_put(&obj->obj);86 87	if (IS_ERR(dfb))88		return PTR_ERR(dfb);89 90	info = drm_fb_helper_alloc_info(fbh);91	if (IS_ERR(info)) {92		ret = PTR_ERR(info);93		goto err_fballoc;94	}95 96	info->fbops = &armada_fb_ops;97	info->fix.smem_start = obj->phys_addr;98	info->fix.smem_len = obj->obj.size;99	info->screen_size = obj->obj.size;100	info->screen_base = ptr;101	fbh->fb = &dfb->fb;102 103	drm_fb_helper_fill_info(info, fbh, sizes);104 105	DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n",106		dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8,107		(unsigned long long)obj->phys_addr);108 109	return 0;110 111 err_fballoc:112	dfb->fb.funcs->destroy(&dfb->fb);113	return ret;114}115 116static int armada_fb_probe(struct drm_fb_helper *fbh,117	struct drm_fb_helper_surface_size *sizes)118{119	int ret = 0;120 121	if (!fbh->fb) {122		ret = armada_fbdev_create(fbh, sizes);123		if (ret == 0)124			ret = 1;125	}126	return ret;127}128 129static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {130	.fb_probe	= armada_fb_probe,131};132 133/*134 * Fbdev client and struct drm_client_funcs135 */136 137static void armada_fbdev_client_unregister(struct drm_client_dev *client)138{139	struct drm_fb_helper *fbh = drm_fb_helper_from_client(client);140 141	if (fbh->info) {142		drm_fb_helper_unregister_info(fbh);143	} else {144		drm_client_release(&fbh->client);145		drm_fb_helper_unprepare(fbh);146		kfree(fbh);147	}148}149 150static int armada_fbdev_client_restore(struct drm_client_dev *client)151{152	drm_fb_helper_lastclose(client->dev);153 154	return 0;155}156 157static int armada_fbdev_client_hotplug(struct drm_client_dev *client)158{159	struct drm_fb_helper *fbh = drm_fb_helper_from_client(client);160	struct drm_device *dev = client->dev;161	int ret;162 163	if (dev->fb_helper)164		return drm_fb_helper_hotplug_event(dev->fb_helper);165 166	ret = drm_fb_helper_init(dev, fbh);167	if (ret)168		goto err_drm_err;169 170	if (!drm_drv_uses_atomic_modeset(dev))171		drm_helper_disable_unused_functions(dev);172 173	ret = drm_fb_helper_initial_config(fbh);174	if (ret)175		goto err_drm_fb_helper_fini;176 177	return 0;178 179err_drm_fb_helper_fini:180	drm_fb_helper_fini(fbh);181err_drm_err:182	drm_err(dev, "armada: Failed to setup fbdev emulation (ret=%d)\n", ret);183	return ret;184}185 186static const struct drm_client_funcs armada_fbdev_client_funcs = {187	.owner		= THIS_MODULE,188	.unregister	= armada_fbdev_client_unregister,189	.restore	= armada_fbdev_client_restore,190	.hotplug	= armada_fbdev_client_hotplug,191};192 193void armada_fbdev_setup(struct drm_device *dev)194{195	struct drm_fb_helper *fbh;196	int ret;197 198	drm_WARN(dev, !dev->registered, "Device has not been registered.\n");199	drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");200 201	fbh = kzalloc(sizeof(*fbh), GFP_KERNEL);202	if (!fbh)203		return;204	drm_fb_helper_prepare(dev, fbh, 32, &armada_fb_helper_funcs);205 206	ret = drm_client_init(dev, &fbh->client, "fbdev", &armada_fbdev_client_funcs);207	if (ret) {208		drm_err(dev, "Failed to register client: %d\n", ret);209		goto err_drm_client_init;210	}211 212	drm_client_register(&fbh->client);213 214	return;215 216err_drm_client_init:217	drm_fb_helper_unprepare(fbh);218	kfree(fbh);219	return;220}221