mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 16:53:54 +08:00
drm/nouveau/kms: switch to drm fbdev helpers
This removes support for accelerated fbcon rendering, and fixes a number of races/crashes/issues around suspend/resume/module unload etc. Losing HW accelerated rendering isn't ideal, but it's been significantly reduced in performance since the removal of accelerated scrolling in the kernel anyway - not to mention, can be racey (skips cpu<->gpu sync) from certain contexts. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
99d0701afd
commit
4a16dd9d18
@ -54,10 +54,6 @@ nouveau-y += nouveau_bios.o
|
||||
nouveau-y += nouveau_connector.o
|
||||
nouveau-y += nouveau_display.o
|
||||
nouveau-y += nouveau_dp.o
|
||||
nouveau-y += nouveau_fbcon.o
|
||||
nouveau-y += nv04_fbcon.o
|
||||
nouveau-y += nv50_fbcon.o
|
||||
nouveau-y += nvc0_fbcon.o
|
||||
include $(src)/dispnv04/Kbuild
|
||||
include $(src)/dispnv50/Kbuild
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
@ -37,7 +38,6 @@
|
||||
#include "nouveau_crtc.h"
|
||||
#include "hw.h"
|
||||
#include "nvreg.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "disp.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
@ -915,14 +915,6 @@ nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int x, int y, enum mode_set_atomic state)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(crtc->dev);
|
||||
struct drm_device *dev = drm->dev;
|
||||
|
||||
if (state == ENTER_ATOMIC_MODE_SET)
|
||||
nouveau_fbcon_accel_save_disable(dev);
|
||||
else
|
||||
nouveau_fbcon_accel_restore(dev);
|
||||
|
||||
return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true);
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,6 @@
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_fence.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
|
||||
#include <subdev/bios/dp.h>
|
||||
|
||||
@ -2446,7 +2445,7 @@ nv50_disp_atomic_state_alloc(struct drm_device *dev)
|
||||
static const struct drm_mode_config_funcs
|
||||
nv50_disp_func = {
|
||||
.fb_create = nouveau_user_framebuffer_create,
|
||||
.output_poll_changed = nouveau_fbcon_output_poll_changed,
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = nv50_disp_atomic_check,
|
||||
.atomic_commit = nv50_disp_atomic_commit,
|
||||
.atomic_state_alloc = nv50_disp_atomic_state_alloc,
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_gem.h"
|
||||
#include "nouveau_connector.h"
|
||||
@ -392,7 +391,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
|
||||
|
||||
static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
|
||||
.fb_create = nouveau_user_framebuffer_create,
|
||||
.output_poll_changed = nouveau_fbcon_output_poll_changed,
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
};
|
||||
|
||||
|
||||
@ -788,6 +787,9 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
|
||||
/* Disable console. */
|
||||
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, true);
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev)) {
|
||||
if (!runtime) {
|
||||
disp->suspend = drm_atomic_helper_suspend(dev);
|
||||
@ -815,8 +817,10 @@ nouveau_display_resume(struct drm_device *dev, bool runtime)
|
||||
drm_atomic_helper_resume(dev, disp->suspend);
|
||||
disp->suspend = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable console. */
|
||||
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, false);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <drm/drm_aperture.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_ttm_helper.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
@ -62,7 +63,6 @@
|
||||
#include "nouveau_bios.h"
|
||||
#include "nouveau_ioctl.h"
|
||||
#include "nouveau_abi16.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_fence.h"
|
||||
#include "nouveau_debugfs.h"
|
||||
#include "nouveau_usif.h"
|
||||
@ -611,7 +611,6 @@ nouveau_drm_device_init(struct drm_device *dev)
|
||||
nouveau_hwmon_init(dev);
|
||||
nouveau_svm_init(drm);
|
||||
nouveau_dmem_init(drm);
|
||||
nouveau_fbcon_init(dev);
|
||||
nouveau_led_init(dev);
|
||||
|
||||
if (nouveau_pmops_runtime()) {
|
||||
@ -655,7 +654,6 @@ nouveau_drm_device_fini(struct drm_device *dev)
|
||||
}
|
||||
|
||||
nouveau_led_fini(dev);
|
||||
nouveau_fbcon_fini(dev);
|
||||
nouveau_dmem_fini(drm);
|
||||
nouveau_svm_fini(drm);
|
||||
nouveau_hwmon_fini(dev);
|
||||
@ -809,6 +807,11 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
|
||||
if (ret)
|
||||
goto fail_drm_dev_init;
|
||||
|
||||
if (nouveau_drm(drm_dev)->client.device.info.ram_size <= 32 * 1024 * 1024)
|
||||
drm_fbdev_generic_setup(drm_dev, 8);
|
||||
else
|
||||
drm_fbdev_generic_setup(drm_dev, 32);
|
||||
|
||||
quirk_broken_nv_runpm(pdev);
|
||||
return 0;
|
||||
|
||||
@ -865,8 +868,6 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
|
||||
nouveau_led_suspend(dev);
|
||||
|
||||
if (dev->mode_config.num_crtc) {
|
||||
NV_DEBUG(drm, "suspending console...\n");
|
||||
nouveau_fbcon_set_suspend(dev, 1);
|
||||
NV_DEBUG(drm, "suspending display...\n");
|
||||
ret = nouveau_display_suspend(dev, runtime);
|
||||
if (ret)
|
||||
@ -940,8 +941,6 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
|
||||
if (dev->mode_config.num_crtc) {
|
||||
NV_DEBUG(drm, "resuming display...\n");
|
||||
nouveau_display_resume(dev, runtime);
|
||||
NV_DEBUG(drm, "resuming console...\n");
|
||||
nouveau_fbcon_set_suspend(dev, 0);
|
||||
}
|
||||
|
||||
nouveau_led_resume(dev);
|
||||
@ -1296,7 +1295,6 @@ static void nouveau_display_options(void)
|
||||
DRM_DEBUG_DRIVER("... tv_disable : %d\n", nouveau_tv_disable);
|
||||
DRM_DEBUG_DRIVER("... ignorelid : %d\n", nouveau_ignorelid);
|
||||
DRM_DEBUG_DRIVER("... duallink : %d\n", nouveau_duallink);
|
||||
DRM_DEBUG_DRIVER("... nofbaccel : %d\n", nouveau_nofbaccel);
|
||||
DRM_DEBUG_DRIVER("... config : %s\n", nouveau_config);
|
||||
DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug);
|
||||
DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel);
|
||||
|
@ -183,7 +183,6 @@ struct nouveau_drm {
|
||||
struct nouveau_channel *cechan;
|
||||
struct nouveau_channel *channel;
|
||||
struct nvkm_gpuobj *notify;
|
||||
struct nouveau_fbdev *fbcon;
|
||||
struct nvif_object ntfy;
|
||||
|
||||
/* nv10-nv40 tiling regions */
|
||||
@ -198,8 +197,6 @@ struct nouveau_drm {
|
||||
struct work_struct hpd_work;
|
||||
spinlock_t hpd_lock;
|
||||
u32 hpd_pending;
|
||||
struct work_struct fbcon_work;
|
||||
int fbcon_new_state;
|
||||
#ifdef CONFIG_ACPI
|
||||
struct notifier_block acpi_nb;
|
||||
#endif
|
||||
|
@ -1,614 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2007 David Airlie
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* David Airlie
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_gem.h"
|
||||
#include "nouveau_bo.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_chan.h"
|
||||
#include "nouveau_vmm.h"
|
||||
|
||||
#include "nouveau_crtc.h"
|
||||
|
||||
MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
|
||||
int nouveau_nofbaccel = 0;
|
||||
module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
|
||||
|
||||
MODULE_PARM_DESC(fbcon_bpp, "fbcon bits-per-pixel (default: auto)");
|
||||
static int nouveau_fbcon_bpp;
|
||||
module_param_named(fbcon_bpp, nouveau_fbcon_bpp, int, 0400);
|
||||
|
||||
static void
|
||||
nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_fillrect(info, rect);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_fillrect(info, rect);
|
||||
else
|
||||
ret = nvc0_fbcon_fillrect(info, rect);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_fillrect(info, rect);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_copyarea(info, image);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_copyarea(info, image);
|
||||
else
|
||||
ret = nvc0_fbcon_copyarea(info, image);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_copyarea(info, image);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret;
|
||||
|
||||
if (info->state != FBINFO_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
|
||||
mutex_trylock(&drm->client.mutex)) {
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_imageblit(info, image);
|
||||
else
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_imageblit(info, image);
|
||||
else
|
||||
ret = nvc0_fbcon_imageblit(info, image);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
if (ret != -ENODEV)
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
drm_fb_helper_cfb_imageblit(info, image);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_sync(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
int ret;
|
||||
|
||||
if (!chan || !chan->accel_done || in_interrupt() ||
|
||||
info->state != FBINFO_STATE_RUNNING ||
|
||||
info->flags & FBINFO_HWACCEL_DISABLED)
|
||||
return 0;
|
||||
|
||||
if (!mutex_trylock(&drm->client.mutex))
|
||||
return 0;
|
||||
|
||||
ret = nouveau_channel_idle(chan);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
if (ret) {
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
chan->accel_done = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_open(struct fb_info *info, int user)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
int ret = pm_runtime_get_sync(drm->dev->dev);
|
||||
if (ret < 0 && ret != -EACCES) {
|
||||
pm_runtime_put(drm->dev->dev);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
pm_runtime_put(drm->dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fb_ops nouveau_fbcon_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_open = nouveau_fbcon_open,
|
||||
.fb_release = nouveau_fbcon_release,
|
||||
.fb_fillrect = nouveau_fbcon_fillrect,
|
||||
.fb_copyarea = nouveau_fbcon_copyarea,
|
||||
.fb_imageblit = nouveau_fbcon_imageblit,
|
||||
.fb_sync = nouveau_fbcon_sync,
|
||||
};
|
||||
|
||||
static const struct fb_ops nouveau_fbcon_sw_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_open = nouveau_fbcon_open,
|
||||
.fb_release = nouveau_fbcon_release,
|
||||
.fb_fillrect = drm_fb_helper_cfb_fillrect,
|
||||
.fb_copyarea = drm_fb_helper_cfb_copyarea,
|
||||
.fb_imageblit = drm_fb_helper_cfb_imageblit,
|
||||
};
|
||||
|
||||
void
|
||||
nouveau_fbcon_accel_save_disable(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
if (drm->fbcon && drm->fbcon->helper.fbdev) {
|
||||
drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
|
||||
drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_accel_restore(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
if (drm->fbcon && drm->fbcon->helper.fbdev) {
|
||||
drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_accel_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
if (fbcon && drm->channel) {
|
||||
console_lock();
|
||||
if (fbcon->helper.fbdev)
|
||||
fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
console_unlock();
|
||||
nouveau_channel_idle(drm->channel);
|
||||
nvif_object_dtor(&fbcon->twod);
|
||||
nvif_object_dtor(&fbcon->blit);
|
||||
nvif_object_dtor(&fbcon->gdi);
|
||||
nvif_object_dtor(&fbcon->patt);
|
||||
nvif_object_dtor(&fbcon->rop);
|
||||
nvif_object_dtor(&fbcon->clip);
|
||||
nvif_object_dtor(&fbcon->surf2d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_accel_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
struct fb_info *info = fbcon->helper.fbdev;
|
||||
int ret;
|
||||
|
||||
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA)
|
||||
ret = nv04_fbcon_accel_init(info);
|
||||
else
|
||||
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
|
||||
ret = nv50_fbcon_accel_init(info);
|
||||
else
|
||||
ret = nvc0_fbcon_accel_init(info);
|
||||
|
||||
if (ret == 0)
|
||||
info->fbops = &nouveau_fbcon_ops;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct fb_info *info = fbcon->helper.fbdev;
|
||||
struct fb_fillrect rect;
|
||||
|
||||
/* Clear the entire fbcon. The drm will program every connector
|
||||
* with it's preferred mode. If the sizes differ, one display will
|
||||
* quite likely have garbage around the console.
|
||||
*/
|
||||
rect.dx = rect.dy = 0;
|
||||
rect.width = info->var.xres_virtual;
|
||||
rect.height = info->var.yres_virtual;
|
||||
rect.color = 0;
|
||||
rect.rop = ROP_COPY;
|
||||
info->fbops->fb_fillrect(info, &rect);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_create(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon =
|
||||
container_of(helper, struct nouveau_fbdev, helper);
|
||||
struct drm_device *dev = fbcon->helper.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
struct fb_info *info;
|
||||
struct drm_framebuffer *fb;
|
||||
struct nouveau_channel *chan;
|
||||
struct nouveau_bo *nvbo;
|
||||
struct drm_mode_fb_cmd2 mode_cmd = {};
|
||||
int ret;
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
|
||||
mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3);
|
||||
mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256);
|
||||
|
||||
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||
sizes->surface_depth);
|
||||
|
||||
ret = nouveau_gem_new(&drm->client, mode_cmd.pitches[0] *
|
||||
mode_cmd.height, 0, NOUVEAU_GEM_DOMAIN_VRAM,
|
||||
0, 0x0000, &nvbo);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate framebuffer\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nouveau_framebuffer_new(dev, &mode_cmd, &nvbo->bo.base, &fb);
|
||||
if (ret)
|
||||
goto out_unref;
|
||||
|
||||
ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to pin fb: %d\n", ret);
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
ret = nouveau_bo_map(nvbo);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to map fb: %d\n", ret);
|
||||
goto out_unpin;
|
||||
}
|
||||
|
||||
chan = nouveau_nofbaccel ? NULL : drm->channel;
|
||||
if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nouveau_vma_new(nvbo, chan->vmm, &fbcon->vma);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
|
||||
chan = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
info = drm_fb_helper_alloc_fbi(helper);
|
||||
if (IS_ERR(info)) {
|
||||
ret = PTR_ERR(info);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* setup helper */
|
||||
fbcon->helper.fb = fb;
|
||||
|
||||
if (!chan)
|
||||
info->flags = FBINFO_HWACCEL_DISABLED;
|
||||
else
|
||||
info->flags = FBINFO_HWACCEL_COPYAREA |
|
||||
FBINFO_HWACCEL_FILLRECT |
|
||||
FBINFO_HWACCEL_IMAGEBLIT;
|
||||
info->fbops = &nouveau_fbcon_sw_ops;
|
||||
info->fix.smem_start = nvbo->bo.resource->bus.offset;
|
||||
info->fix.smem_len = nvbo->bo.base.size;
|
||||
|
||||
info->screen_base = nvbo_kmap_obj_iovirtual(nvbo);
|
||||
info->screen_size = nvbo->bo.base.size;
|
||||
|
||||
drm_fb_helper_fill_info(info, &fbcon->helper, sizes);
|
||||
|
||||
/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
|
||||
|
||||
if (chan)
|
||||
nouveau_fbcon_accel_init(dev);
|
||||
nouveau_fbcon_zfill(dev, fbcon);
|
||||
|
||||
/* To allow resizeing without swapping buffers */
|
||||
NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n",
|
||||
fb->width, fb->height, nvbo->offset, nvbo);
|
||||
|
||||
if (dev_is_pci(dev->dev))
|
||||
vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), info);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
if (chan)
|
||||
nouveau_vma_del(&fbcon->vma);
|
||||
nouveau_bo_unmap(nvbo);
|
||||
out_unpin:
|
||||
nouveau_bo_unpin(nvbo);
|
||||
out_unref:
|
||||
nouveau_bo_ref(NULL, &nvbo);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct drm_framebuffer *fb = fbcon->helper.fb;
|
||||
struct nouveau_bo *nvbo;
|
||||
|
||||
drm_fb_helper_unregister_fbi(&fbcon->helper);
|
||||
drm_fb_helper_fini(&fbcon->helper);
|
||||
|
||||
if (fb && fb->obj[0]) {
|
||||
nvbo = nouveau_gem_object(fb->obj[0]);
|
||||
nouveau_vma_del(&fbcon->vma);
|
||||
nouveau_bo_unmap(nvbo);
|
||||
nouveau_bo_unpin(nvbo);
|
||||
drm_framebuffer_put(fb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nouveau_fbcon_gpu_lockup(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *fbcon = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
|
||||
|
||||
NV_ERROR(drm, "GPU lockup - switching to software fbcon\n");
|
||||
info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
}
|
||||
|
||||
static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
|
||||
.fb_probe = nouveau_fbcon_create,
|
||||
};
|
||||
|
||||
static void
|
||||
nouveau_fbcon_set_suspend_work(struct work_struct *work)
|
||||
{
|
||||
struct nouveau_drm *drm = container_of(work, typeof(*drm), fbcon_work);
|
||||
int state = READ_ONCE(drm->fbcon_new_state);
|
||||
|
||||
if (state == FBINFO_STATE_RUNNING)
|
||||
pm_runtime_get_sync(drm->dev->dev);
|
||||
|
||||
console_lock();
|
||||
if (state == FBINFO_STATE_RUNNING)
|
||||
nouveau_fbcon_accel_restore(drm->dev);
|
||||
drm_fb_helper_set_suspend(&drm->fbcon->helper, state);
|
||||
if (state != FBINFO_STATE_RUNNING)
|
||||
nouveau_fbcon_accel_save_disable(drm->dev);
|
||||
console_unlock();
|
||||
|
||||
if (state == FBINFO_STATE_RUNNING) {
|
||||
nouveau_fbcon_hotplug_resume(drm->fbcon);
|
||||
pm_runtime_mark_last_busy(drm->dev->dev);
|
||||
pm_runtime_put_autosuspend(drm->dev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
if (!drm->fbcon)
|
||||
return;
|
||||
|
||||
drm->fbcon_new_state = state;
|
||||
/* Since runtime resume can happen as a result of a sysfs operation,
|
||||
* it's possible we already have the console locked. So handle fbcon
|
||||
* init/deinit from a seperate work thread
|
||||
*/
|
||||
schedule_work(&drm->fbcon_work);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon = drm->fbcon;
|
||||
int ret;
|
||||
|
||||
if (!fbcon)
|
||||
return;
|
||||
|
||||
mutex_lock(&fbcon->hotplug_lock);
|
||||
|
||||
ret = pm_runtime_get(dev->dev);
|
||||
if (ret == 1 || ret == -EACCES) {
|
||||
drm_fb_helper_hotplug_event(&fbcon->helper);
|
||||
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
} else if (ret == 0) {
|
||||
/* If the GPU was already in the process of suspending before
|
||||
* this event happened, then we can't block here as we'll
|
||||
* deadlock the runtime pmops since they wait for us to
|
||||
* finish. So, just defer this event for when we runtime
|
||||
* resume again. It will be handled by fbcon_work.
|
||||
*/
|
||||
NV_DEBUG(drm, "fbcon HPD event deferred until runtime resume\n");
|
||||
fbcon->hotplug_waiting = true;
|
||||
pm_runtime_put_noidle(drm->dev->dev);
|
||||
} else {
|
||||
DRM_WARN("fbcon HPD event lost due to RPM failure: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&fbcon->hotplug_lock);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon)
|
||||
{
|
||||
struct nouveau_drm *drm;
|
||||
|
||||
if (!fbcon)
|
||||
return;
|
||||
drm = nouveau_drm(fbcon->helper.dev);
|
||||
|
||||
mutex_lock(&fbcon->hotplug_lock);
|
||||
if (fbcon->hotplug_waiting) {
|
||||
fbcon->hotplug_waiting = false;
|
||||
|
||||
NV_DEBUG(drm, "Handling deferred fbcon HPD events\n");
|
||||
drm_fb_helper_hotplug_event(&fbcon->helper);
|
||||
}
|
||||
mutex_unlock(&fbcon->hotplug_lock);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_fbcon_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fbdev *fbcon;
|
||||
int preferred_bpp = nouveau_fbcon_bpp;
|
||||
int ret;
|
||||
|
||||
if (!dev->mode_config.num_crtc ||
|
||||
(to_pci_dev(dev->dev)->class >> 8) != PCI_CLASS_DISPLAY_VGA)
|
||||
return 0;
|
||||
|
||||
fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
|
||||
if (!fbcon)
|
||||
return -ENOMEM;
|
||||
|
||||
drm->fbcon = fbcon;
|
||||
INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work);
|
||||
mutex_init(&fbcon->hotplug_lock);
|
||||
|
||||
drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, &fbcon->helper);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
if (preferred_bpp != 8 && preferred_bpp != 16 && preferred_bpp != 32) {
|
||||
if (drm->client.device.info.ram_size <= 32 * 1024 * 1024)
|
||||
preferred_bpp = 8;
|
||||
else
|
||||
if (drm->client.device.info.ram_size <= 64 * 1024 * 1024)
|
||||
preferred_bpp = 16;
|
||||
else
|
||||
preferred_bpp = 32;
|
||||
}
|
||||
|
||||
/* disable all the possible outputs/crtcs before entering KMS mode */
|
||||
if (!drm_drv_uses_atomic_modeset(dev))
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
|
||||
ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
|
||||
if (ret)
|
||||
goto fini;
|
||||
|
||||
if (fbcon->helper.fbdev)
|
||||
fbcon->helper.fbdev->pixmap.buf_align = 4;
|
||||
return 0;
|
||||
|
||||
fini:
|
||||
drm_fb_helper_fini(&fbcon->helper);
|
||||
free:
|
||||
kfree(fbcon);
|
||||
drm->fbcon = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fbcon_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
if (!drm->fbcon)
|
||||
return;
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
nouveau_fbcon_accel_fini(dev);
|
||||
nouveau_fbcon_destroy(dev, drm->fbcon);
|
||||
kfree(drm->fbcon);
|
||||
drm->fbcon = NULL;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NOUVEAU_FBCON_H__
|
||||
#define __NOUVEAU_FBCON_H__
|
||||
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include "nouveau_display.h"
|
||||
|
||||
struct nouveau_vma;
|
||||
|
||||
struct nouveau_fbdev {
|
||||
struct drm_fb_helper helper; /* must be first */
|
||||
unsigned int saved_flags;
|
||||
struct nvif_object surf2d;
|
||||
struct nvif_object clip;
|
||||
struct nvif_object rop;
|
||||
struct nvif_object patt;
|
||||
struct nvif_object gdi;
|
||||
struct nvif_object blit;
|
||||
struct nvif_object twod;
|
||||
struct nouveau_vma *vma;
|
||||
|
||||
struct mutex hotplug_lock;
|
||||
bool hotplug_waiting;
|
||||
};
|
||||
|
||||
void nouveau_fbcon_restore(void);
|
||||
|
||||
int nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
|
||||
int nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
|
||||
int nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
|
||||
int nv04_fbcon_accel_init(struct fb_info *info);
|
||||
|
||||
int nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
|
||||
int nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
|
||||
int nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
|
||||
int nv50_fbcon_accel_init(struct fb_info *info);
|
||||
|
||||
int nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
|
||||
int nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
|
||||
int nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
|
||||
int nvc0_fbcon_accel_init(struct fb_info *info);
|
||||
|
||||
void nouveau_fbcon_gpu_lockup(struct fb_info *info);
|
||||
|
||||
int nouveau_fbcon_init(struct drm_device *dev);
|
||||
void nouveau_fbcon_fini(struct drm_device *dev);
|
||||
void nouveau_fbcon_set_suspend(struct drm_device *dev, int state);
|
||||
void nouveau_fbcon_accel_save_disable(struct drm_device *dev);
|
||||
void nouveau_fbcon_accel_restore(struct drm_device *dev);
|
||||
|
||||
void nouveau_fbcon_output_poll_changed(struct drm_device *dev);
|
||||
void nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon);
|
||||
extern int nouveau_nofbaccel;
|
||||
|
||||
#endif /* __NV50_FBCON_H__ */
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_acpi.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_vga.h"
|
||||
|
||||
static unsigned int
|
||||
|
@ -1,259 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Ben Skeggs
|
||||
* Copyright 2008 Stuart Bennett
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#define NVIF_DEBUG_PRINT_DISABLE
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
|
||||
#include <nvif/push006c.h>
|
||||
|
||||
int
|
||||
nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret;
|
||||
|
||||
ret = PUSH_WAIT(push, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_NVSQ(push, NV05F, 0x0300, (region->sy << 16) | region->sx,
|
||||
0x0304, (region->dy << 16) | region->dx,
|
||||
0x0308, (region->height << 16) | region->width);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret;
|
||||
|
||||
ret = PUSH_WAIT(push, 7);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_NVSQ(push, NV04A, 0x02fc, (rect->rop != ROP_COPY) ? 1 : 3);
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR)
|
||||
PUSH_NVSQ(push, NV04A, 0x03fc, ((uint32_t *)info->pseudo_palette)[rect->color]);
|
||||
else
|
||||
PUSH_NVSQ(push, NV04A, 0x03fc, rect->color);
|
||||
PUSH_NVSQ(push, NV04A, 0x0400, (rect->dx << 16) | rect->dy,
|
||||
0x0404, (rect->width << 16) | rect->height);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
uint32_t fg;
|
||||
uint32_t bg;
|
||||
uint32_t dsize;
|
||||
uint32_t *data = (uint32_t *)image->data;
|
||||
int ret;
|
||||
|
||||
if (image->depth != 1)
|
||||
return -ENODEV;
|
||||
|
||||
ret = PUSH_WAIT(push, 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
|
||||
bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
|
||||
} else {
|
||||
fg = image->fg_color;
|
||||
bg = image->bg_color;
|
||||
}
|
||||
|
||||
PUSH_NVSQ(push, NV04A, 0x0be4, (image->dy << 16) | (image->dx & 0xffff),
|
||||
0x0be8, ((image->dy + image->height) << 16) |
|
||||
((image->dx + image->width) & 0xffff),
|
||||
0x0bec, bg,
|
||||
0x0bf0, fg,
|
||||
0x0bf4, (image->height << 16) | ALIGN(image->width, 8),
|
||||
0x0bf8, (image->height << 16) | image->width,
|
||||
0x0bfc, (image->dy << 16) | (image->dx & 0xffff));
|
||||
|
||||
dsize = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
|
||||
while (dsize) {
|
||||
int iter_len = dsize > 128 ? 128 : dsize;
|
||||
|
||||
ret = PUSH_WAIT(push, iter_len + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_NVSQ(push, NV04A, 0x0c00, data, iter_len);
|
||||
data += iter_len;
|
||||
dsize -= iter_len;
|
||||
}
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv04_fbcon_accel_init(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct drm_device *dev = nfbdev->helper.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
struct nvkm_device *nvkm_device = nvxx_device(&drm->client.device);
|
||||
resource_size_t fb_base = nvkm_device->func->resource_addr(nvkm_device, 1);
|
||||
int surface_fmt, pattern_fmt, rect_fmt;
|
||||
int ret;
|
||||
|
||||
switch (info->var.bits_per_pixel) {
|
||||
case 8:
|
||||
surface_fmt = 1;
|
||||
pattern_fmt = 3;
|
||||
rect_fmt = 3;
|
||||
break;
|
||||
case 16:
|
||||
surface_fmt = 4;
|
||||
pattern_fmt = 1;
|
||||
rect_fmt = 1;
|
||||
break;
|
||||
case 32:
|
||||
switch (info->var.transp.length) {
|
||||
case 0: /* depth 24 */
|
||||
case 8: /* depth 32 */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
surface_fmt = 6;
|
||||
pattern_fmt = 3;
|
||||
rect_fmt = 3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconCtxSurf2d", 0x0062,
|
||||
device->info.family >= NV_DEVICE_INFO_V0_CELSIUS ?
|
||||
0x0062 : 0x0042, NULL, 0, &nfbdev->surf2d);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconCtxClip", 0x0019, 0x0019,
|
||||
NULL, 0, &nfbdev->clip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconCtxRop", 0x0043, 0x0043,
|
||||
NULL, 0, &nfbdev->rop);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconCtxPatt", 0x0044, 0x0044,
|
||||
NULL, 0, &nfbdev->patt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconGdiRectText", 0x004a, 0x004a,
|
||||
NULL, 0, &nfbdev->gdi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconImageBlit", 0x005f,
|
||||
device->info.chipset >= 0x11 ? 0x009f : 0x005f,
|
||||
NULL, 0, &nfbdev->blit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (PUSH_WAIT(push, 49 + (device->info.chipset >= 0x11 ? 4 : 0))) {
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PUSH_NVSQ(push, NV042, 0x0000, nfbdev->surf2d.handle);
|
||||
PUSH_NVSQ(push, NV042, 0x0184, chan->vram.handle,
|
||||
0x0188, chan->vram.handle);
|
||||
PUSH_NVSQ(push, NV042, 0x0300, surface_fmt,
|
||||
0x0304, info->fix.line_length | (info->fix.line_length << 16),
|
||||
0x0308, info->fix.smem_start - fb_base,
|
||||
0x030c, info->fix.smem_start - fb_base);
|
||||
|
||||
PUSH_NVSQ(push, NV043, 0x0000, nfbdev->rop.handle);
|
||||
PUSH_NVSQ(push, NV043, 0x0300, 0x55);
|
||||
|
||||
PUSH_NVSQ(push, NV044, 0x0000, nfbdev->patt.handle);
|
||||
PUSH_NVSQ(push, NV044, 0x0300, pattern_fmt,
|
||||
#ifdef __BIG_ENDIAN
|
||||
0x0304, 2,
|
||||
#else
|
||||
0x0304, 1,
|
||||
#endif
|
||||
0x0308, 0,
|
||||
0x030c, 1,
|
||||
0x0310, ~0,
|
||||
0x0314, ~0,
|
||||
0x0318, ~0,
|
||||
0x031c, ~0);
|
||||
|
||||
PUSH_NVSQ(push, NV019, 0x0000, nfbdev->clip.handle);
|
||||
PUSH_NVSQ(push, NV019, 0x0300, 0,
|
||||
0x0304, (info->var.yres_virtual << 16) | info->var.xres_virtual);
|
||||
|
||||
PUSH_NVSQ(push, NV05F, 0x0000, nfbdev->blit.handle);
|
||||
PUSH_NVSQ(push, NV05F, 0x019c, nfbdev->surf2d.handle);
|
||||
PUSH_NVSQ(push, NV05F, 0x02fc, 3);
|
||||
if (nfbdev->blit.oclass == 0x009f) {
|
||||
PUSH_NVSQ(push, NV09F, 0x0120, 0,
|
||||
0x0124, 1,
|
||||
0x0128, 2);
|
||||
}
|
||||
|
||||
PUSH_NVSQ(push, NV04A, 0x0000, nfbdev->gdi.handle);
|
||||
PUSH_NVSQ(push, NV04A, 0x0198, nfbdev->surf2d.handle);
|
||||
PUSH_NVSQ(push, NV04A, 0x0188, nfbdev->patt.handle,
|
||||
0x018c, nfbdev->rop.handle);
|
||||
PUSH_NVSQ(push, NV04A, 0x0304, 1);
|
||||
PUSH_NVSQ(push, NV04A, 0x0300, rect_fmt);
|
||||
PUSH_NVSQ(push, NV04A, 0x02fc, 3);
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#define NVIF_DEBUG_PRINT_DISABLE
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_vmm.h"
|
||||
|
||||
#include <nvif/push206e.h>
|
||||
|
||||
#include <nvhw/class/cl502d.h>
|
||||
|
||||
int
|
||||
nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
u32 colour;
|
||||
int ret;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR)
|
||||
colour = ((uint32_t *)info->pseudo_palette)[rect->color];
|
||||
else
|
||||
colour = rect->color;
|
||||
|
||||
ret = PUSH_WAIT(push, rect->rop == ROP_COPY ? 7 : 11);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rect->rop != ROP_COPY) {
|
||||
PUSH_MTHD(push, NV502D, SET_OPERATION,
|
||||
NVDEF(NV502D, SET_OPERATION, V, ROP_AND));
|
||||
}
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_RENDER_SOLID_PRIM_COLOR, colour);
|
||||
|
||||
PUSH_MTHD(push, NV502D, RENDER_SOLID_PRIM_POINT_SET_X(0), rect->dx,
|
||||
RENDER_SOLID_PRIM_POINT_Y(0), rect->dy,
|
||||
RENDER_SOLID_PRIM_POINT_SET_X(1), rect->dx + rect->width,
|
||||
RENDER_SOLID_PRIM_POINT_Y(1), rect->dy + rect->height);
|
||||
|
||||
if (rect->rop != ROP_COPY) {
|
||||
PUSH_MTHD(push, NV502D, SET_OPERATION,
|
||||
NVDEF(NV502D, SET_OPERATION, V, SRCCOPY));
|
||||
}
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret;
|
||||
|
||||
ret = PUSH_WAIT(push, 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_MTHD(push, NV502D, WAIT_FOR_IDLE, 0);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_MEMORY_DST_X0, region->dx,
|
||||
SET_PIXELS_FROM_MEMORY_DST_Y0, region->dy,
|
||||
SET_PIXELS_FROM_MEMORY_DST_WIDTH, region->width,
|
||||
SET_PIXELS_FROM_MEMORY_DST_HEIGHT, region->height);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_MEMORY_SRC_X0_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_SRC_X0_INT, region->sx,
|
||||
SET_PIXELS_FROM_MEMORY_SRC_Y0_FRAC, 0,
|
||||
PIXELS_FROM_MEMORY_SRC_Y0_INT, region->sy);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
uint32_t dwords, *data = (uint32_t *)image->data;
|
||||
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
|
||||
uint32_t *palette = info->pseudo_palette, bg, fg;
|
||||
int ret;
|
||||
|
||||
if (image->depth != 1)
|
||||
return -ENODEV;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
bg = palette[image->bg_color] | mask;
|
||||
fg = palette[image->fg_color] | mask;
|
||||
} else {
|
||||
bg = image->bg_color;
|
||||
fg = image->fg_color;
|
||||
}
|
||||
|
||||
ret = PUSH_WAIT(push, 11);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_COLOR0, bg,
|
||||
SET_PIXELS_FROM_CPU_COLOR1, fg);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_SRC_WIDTH, image->width,
|
||||
SET_PIXELS_FROM_CPU_SRC_HEIGHT, image->height);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_DST_X0_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DST_X0_INT, image->dx,
|
||||
SET_PIXELS_FROM_CPU_DST_Y0_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DST_Y0_INT, image->dy);
|
||||
|
||||
dwords = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
|
||||
while (dwords) {
|
||||
int count = dwords > 2047 ? 2047 : dwords;
|
||||
|
||||
ret = PUSH_WAIT(push, count + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwords -= count;
|
||||
|
||||
PUSH_NINC(push, NV502D, PIXELS_FROM_CPU_DATA, data, count);
|
||||
data += count;
|
||||
}
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_fbcon_accel_init(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct drm_device *dev = nfbdev->helper.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret, format;
|
||||
|
||||
switch (info->var.bits_per_pixel) {
|
||||
case 8:
|
||||
format = NV502D_SET_DST_FORMAT_V_Y8;
|
||||
break;
|
||||
case 15:
|
||||
format = NV502D_SET_DST_FORMAT_V_X1R5G5B5;
|
||||
break;
|
||||
case 16:
|
||||
format = NV502D_SET_DST_FORMAT_V_R5G6B5;
|
||||
break;
|
||||
case 32:
|
||||
switch (info->var.transp.length) {
|
||||
case 0: /* depth 24 */
|
||||
case 8: /* depth 32, just use 24.. */
|
||||
format = NV502D_SET_DST_FORMAT_V_X8R8G8B8;
|
||||
break;
|
||||
case 2: /* depth 30 */
|
||||
format = NV502D_SET_DST_FORMAT_V_A2B10G10R10;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconTwoD", 0x502d, 0x502d,
|
||||
NULL, 0, &nfbdev->twod);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = PUSH_WAIT(push, 56);
|
||||
if (ret) {
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_OBJECT, nfbdev->twod.handle);
|
||||
PUSH_MTHD(push, NV502D, SET_DST_CONTEXT_DMA, chan->vram.handle,
|
||||
SET_SRC_CONTEXT_DMA, chan->vram.handle,
|
||||
SET_SEMAPHORE_CONTEXT_DMA, chan->vram.handle);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_DST_FORMAT,
|
||||
NVVAL(NV502D, SET_DST_FORMAT, V, format),
|
||||
|
||||
SET_DST_MEMORY_LAYOUT,
|
||||
NVDEF(NV502D, SET_DST_MEMORY_LAYOUT, V, PITCH));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_DST_PITCH, info->fix.line_length,
|
||||
SET_DST_WIDTH, info->var.xres_virtual,
|
||||
SET_DST_HEIGHT, info->var.yres_virtual,
|
||||
|
||||
SET_DST_OFFSET_UPPER,
|
||||
NVVAL(NV502D, SET_DST_OFFSET_UPPER, V, upper_32_bits(nfbdev->vma->addr)),
|
||||
|
||||
SET_DST_OFFSET_LOWER,
|
||||
NVVAL(NV502D, SET_DST_OFFSET_LOWER, V, lower_32_bits(nfbdev->vma->addr)));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_SRC_FORMAT,
|
||||
NVVAL(NV502D, SET_SRC_FORMAT, V, format),
|
||||
|
||||
SET_SRC_MEMORY_LAYOUT,
|
||||
NVDEF(NV502D, SET_SRC_MEMORY_LAYOUT, V, PITCH));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_SRC_PITCH, info->fix.line_length,
|
||||
SET_SRC_WIDTH, info->var.xres_virtual,
|
||||
SET_SRC_HEIGHT, info->var.yres_virtual,
|
||||
|
||||
SET_SRC_OFFSET_UPPER,
|
||||
NVVAL(NV502D, SET_SRC_OFFSET_UPPER, V, upper_32_bits(nfbdev->vma->addr)),
|
||||
|
||||
SET_SRC_OFFSET_LOWER,
|
||||
NVVAL(NV502D, SET_SRC_OFFSET_LOWER, V, lower_32_bits(nfbdev->vma->addr)));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_CLIP_ENABLE,
|
||||
NVDEF(NV502D, SET_CLIP_ENABLE, V, FALSE));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_ROP,
|
||||
NVVAL(NV502D, SET_ROP, V, 0x55));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_OPERATION,
|
||||
NVDEF(NV502D, SET_OPERATION, V, SRCCOPY));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_MONOCHROME_PATTERN_COLOR_FORMAT,
|
||||
NVDEF(NV502D, SET_MONOCHROME_PATTERN_COLOR_FORMAT, V, A8R8G8B8),
|
||||
|
||||
SET_MONOCHROME_PATTERN_FORMAT,
|
||||
NVDEF(NV502D, SET_MONOCHROME_PATTERN_FORMAT, V, LE_M1));
|
||||
|
||||
PUSH_MTHD(push, NV502D, RENDER_SOLID_PRIM_MODE,
|
||||
NVDEF(NV502D, RENDER_SOLID_PRIM_MODE, V, RECTS),
|
||||
|
||||
SET_RENDER_SOLID_PRIM_COLOR_FORMAT,
|
||||
NVVAL(NV502D, SET_RENDER_SOLID_PRIM_COLOR_FORMAT, V, format));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_DATA_TYPE,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_CPU_DATA_TYPE, V, INDEX),
|
||||
|
||||
SET_PIXELS_FROM_CPU_COLOR_FORMAT,
|
||||
NVVAL(NV502D, SET_PIXELS_FROM_CPU_COLOR_FORMAT, V, format),
|
||||
|
||||
SET_PIXELS_FROM_CPU_INDEX_FORMAT,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_CPU_INDEX_FORMAT, V, I1),
|
||||
|
||||
SET_PIXELS_FROM_CPU_MONO_FORMAT,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_CPU_MONO_FORMAT, V, CGA6_M1),
|
||||
|
||||
SET_PIXELS_FROM_CPU_WRAP,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_CPU_WRAP, V, WRAP_BYTE));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_MONO_OPACITY,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_CPU_MONO_OPACITY, V, OPAQUE));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_CPU_DX_DU_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DX_DU_INT, 1,
|
||||
SET_PIXELS_FROM_CPU_DY_DV_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DY_DV_INT, 1);
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_MEMORY_SAFE_OVERLAP,
|
||||
NVDEF(NV502D, SET_PIXELS_FROM_MEMORY_SAFE_OVERLAP, V, TRUE));
|
||||
|
||||
PUSH_MTHD(push, NV502D, SET_PIXELS_FROM_MEMORY_DU_DX_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_DU_DX_INT, 1,
|
||||
SET_PIXELS_FROM_MEMORY_DV_DY_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_DV_DY_INT, 1);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#define NVIF_DEBUG_PRINT_DISABLE
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_vmm.h"
|
||||
|
||||
#include <nvif/push906f.h>
|
||||
|
||||
#include <nvhw/class/cl902d.h>
|
||||
|
||||
int
|
||||
nvc0_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
u32 colour;
|
||||
int ret;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR)
|
||||
colour = ((uint32_t *)info->pseudo_palette)[rect->color];
|
||||
else
|
||||
colour = rect->color;
|
||||
|
||||
ret = PUSH_WAIT(push, rect->rop == ROP_COPY ? 7 : 9);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rect->rop != ROP_COPY) {
|
||||
PUSH_IMMD(push, NV902D, SET_OPERATION,
|
||||
NVDEF(NV902D, SET_OPERATION, V, ROP_AND));
|
||||
}
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_RENDER_SOLID_PRIM_COLOR, colour);
|
||||
|
||||
PUSH_MTHD(push, NV902D, RENDER_SOLID_PRIM_POINT_SET_X(0), rect->dx,
|
||||
RENDER_SOLID_PRIM_POINT_Y(0), rect->dy,
|
||||
RENDER_SOLID_PRIM_POINT_SET_X(1), rect->dx + rect->width,
|
||||
RENDER_SOLID_PRIM_POINT_Y(1), rect->dy + rect->height);
|
||||
|
||||
if (rect->rop != ROP_COPY) {
|
||||
PUSH_IMMD(push, NV902D, SET_OPERATION,
|
||||
NVDEF(NV902D, SET_OPERATION, V, SRCCOPY));
|
||||
}
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvc0_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret;
|
||||
|
||||
ret = PUSH_WAIT(push, 11);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_IMMD(push, NV902D, WAIT_FOR_IDLE, 0);
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_MEMORY_DST_X0, region->dx,
|
||||
SET_PIXELS_FROM_MEMORY_DST_Y0, region->dy,
|
||||
SET_PIXELS_FROM_MEMORY_DST_WIDTH, region->width,
|
||||
SET_PIXELS_FROM_MEMORY_DST_HEIGHT, region->height);
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_MEMORY_SRC_X0_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_SRC_X0_INT, region->sx,
|
||||
SET_PIXELS_FROM_MEMORY_SRC_Y0_FRAC, 0,
|
||||
PIXELS_FROM_MEMORY_SRC_Y0_INT, region->sy);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct nouveau_drm *drm = nouveau_drm(nfbdev->helper.dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
uint32_t dwords, *data = (uint32_t *)image->data;
|
||||
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
|
||||
uint32_t *palette = info->pseudo_palette, bg, fg;
|
||||
int ret;
|
||||
|
||||
if (image->depth != 1)
|
||||
return -ENODEV;
|
||||
|
||||
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
|
||||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
|
||||
bg = palette[image->bg_color] | mask;
|
||||
fg = palette[image->fg_color] | mask;
|
||||
} else {
|
||||
bg = image->bg_color;
|
||||
fg = image->fg_color;
|
||||
}
|
||||
|
||||
ret = PUSH_WAIT(push, 11);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_CPU_COLOR0, bg,
|
||||
SET_PIXELS_FROM_CPU_COLOR1, fg);
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_CPU_SRC_WIDTH, image->width,
|
||||
SET_PIXELS_FROM_CPU_SRC_HEIGHT, image->height);
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_CPU_DST_X0_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DST_X0_INT, image->dx,
|
||||
SET_PIXELS_FROM_CPU_DST_Y0_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DST_Y0_INT, image->dy);
|
||||
|
||||
dwords = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
|
||||
while (dwords) {
|
||||
int count = dwords > 2047 ? 2047 : dwords;
|
||||
|
||||
ret = PUSH_WAIT(push, count + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwords -= count;
|
||||
|
||||
PUSH_NINC(push, NV902D, PIXELS_FROM_CPU_DATA, data, count);
|
||||
data += count;
|
||||
}
|
||||
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvc0_fbcon_accel_init(struct fb_info *info)
|
||||
{
|
||||
struct nouveau_fbdev *nfbdev = info->par;
|
||||
struct drm_device *dev = nfbdev->helper.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nvif_push *push = chan->chan.push;
|
||||
int ret, format;
|
||||
|
||||
ret = nvif_object_ctor(&chan->user, "fbconTwoD", 0x902d, 0x902d,
|
||||
NULL, 0, &nfbdev->twod);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (info->var.bits_per_pixel) {
|
||||
case 8:
|
||||
format = NV902D_SET_DST_FORMAT_V_Y8;
|
||||
break;
|
||||
case 15:
|
||||
format = NV902D_SET_DST_FORMAT_V_X1R5G5B5;
|
||||
break;
|
||||
case 16:
|
||||
format = NV902D_SET_DST_FORMAT_V_R5G6B5;
|
||||
break;
|
||||
case 32:
|
||||
switch (info->var.transp.length) {
|
||||
case 0: /* depth 24 */
|
||||
case 8: /* depth 32, just use 24.. */
|
||||
format = NV902D_SET_DST_FORMAT_V_X8R8G8B8;
|
||||
break;
|
||||
case 2: /* depth 30 */
|
||||
format = NV902D_SET_DST_FORMAT_V_A2B10G10R10;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = PUSH_WAIT(push, 52);
|
||||
if (ret) {
|
||||
WARN_ON(1);
|
||||
nouveau_fbcon_gpu_lockup(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_OBJECT, nfbdev->twod.handle);
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_DST_FORMAT,
|
||||
NVVAL(NV902D, SET_DST_FORMAT, V, format),
|
||||
|
||||
SET_DST_MEMORY_LAYOUT,
|
||||
NVDEF(NV902D, SET_DST_MEMORY_LAYOUT, V, PITCH));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_DST_PITCH, info->fix.line_length,
|
||||
SET_DST_WIDTH, info->var.xres_virtual,
|
||||
SET_DST_HEIGHT, info->var.yres_virtual,
|
||||
|
||||
SET_DST_OFFSET_UPPER,
|
||||
NVVAL(NV902D, SET_DST_OFFSET_UPPER, V, upper_32_bits(nfbdev->vma->addr)),
|
||||
|
||||
SET_DST_OFFSET_LOWER,
|
||||
NVVAL(NV902D, SET_DST_OFFSET_LOWER, V, lower_32_bits(nfbdev->vma->addr)));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_SRC_FORMAT,
|
||||
NVVAL(NV902D, SET_SRC_FORMAT, V, format),
|
||||
|
||||
SET_SRC_MEMORY_LAYOUT,
|
||||
NVDEF(NV902D, SET_SRC_MEMORY_LAYOUT, V, PITCH));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_SRC_PITCH, info->fix.line_length,
|
||||
SET_SRC_WIDTH, info->var.xres_virtual,
|
||||
SET_SRC_HEIGHT, info->var.yres_virtual,
|
||||
|
||||
SET_SRC_OFFSET_UPPER,
|
||||
NVVAL(NV902D, SET_SRC_OFFSET_UPPER, V, upper_32_bits(nfbdev->vma->addr)),
|
||||
|
||||
SET_SRC_OFFSET_LOWER,
|
||||
NVVAL(NV902D, SET_SRC_OFFSET_LOWER, V, lower_32_bits(nfbdev->vma->addr)));
|
||||
|
||||
PUSH_IMMD(push, NV902D, SET_CLIP_ENABLE,
|
||||
NVDEF(NV902D, SET_CLIP_ENABLE, V, FALSE));
|
||||
|
||||
PUSH_IMMD(push, NV902D, SET_ROP,
|
||||
NVVAL(NV902D, SET_ROP, V, 0x55));
|
||||
|
||||
PUSH_IMMD(push, NV902D, SET_OPERATION,
|
||||
NVDEF(NV902D, SET_OPERATION, V, SRCCOPY));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_MONOCHROME_PATTERN_COLOR_FORMAT,
|
||||
NVDEF(NV902D, SET_MONOCHROME_PATTERN_COLOR_FORMAT, V, A8R8G8B8),
|
||||
|
||||
SET_MONOCHROME_PATTERN_FORMAT,
|
||||
NVDEF(NV902D, SET_MONOCHROME_PATTERN_FORMAT, V, LE_M1));
|
||||
|
||||
PUSH_MTHD(push, NV902D, RENDER_SOLID_PRIM_MODE,
|
||||
NVDEF(NV902D, RENDER_SOLID_PRIM_MODE, V, RECTS),
|
||||
|
||||
SET_RENDER_SOLID_PRIM_COLOR_FORMAT,
|
||||
NVVAL(NV902D, SET_RENDER_SOLID_PRIM_COLOR_FORMAT, V, format));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_CPU_DATA_TYPE,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_CPU_DATA_TYPE, V, INDEX),
|
||||
|
||||
SET_PIXELS_FROM_CPU_COLOR_FORMAT,
|
||||
NVVAL(NV902D, SET_PIXELS_FROM_CPU_COLOR_FORMAT, V, format),
|
||||
|
||||
SET_PIXELS_FROM_CPU_INDEX_FORMAT,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_CPU_INDEX_FORMAT, V, I1),
|
||||
|
||||
SET_PIXELS_FROM_CPU_MONO_FORMAT,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_CPU_MONO_FORMAT, V, CGA6_M1),
|
||||
|
||||
SET_PIXELS_FROM_CPU_WRAP,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_CPU_WRAP, V, WRAP_BYTE));
|
||||
|
||||
PUSH_IMMD(push, NV902D, SET_PIXELS_FROM_CPU_MONO_OPACITY,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_CPU_MONO_OPACITY, V, OPAQUE));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_CPU_DX_DU_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DX_DU_INT, 1,
|
||||
SET_PIXELS_FROM_CPU_DY_DV_FRAC, 0,
|
||||
SET_PIXELS_FROM_CPU_DY_DV_INT, 1);
|
||||
|
||||
PUSH_IMMD(push, NV902D, SET_PIXELS_FROM_MEMORY_SAFE_OVERLAP,
|
||||
NVDEF(NV902D, SET_PIXELS_FROM_MEMORY_SAFE_OVERLAP, V, TRUE));
|
||||
|
||||
PUSH_MTHD(push, NV902D, SET_PIXELS_FROM_MEMORY_DU_DX_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_DU_DX_INT, 1,
|
||||
SET_PIXELS_FROM_MEMORY_DV_DY_FRAC, 0,
|
||||
SET_PIXELS_FROM_MEMORY_DV_DY_INT, 1);
|
||||
PUSH_KICK(push);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user