18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2013 Ilia Mirkin
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
188c2ecf20Sopenharmony_ci * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
198c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
208c2ecf20Sopenharmony_ci * SOFTWARE.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Implementation based on the pre-KMS implementation in xf86-video-nouveau,
238c2ecf20Sopenharmony_ci * written by Arthur Huillet.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
278c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "nouveau_drv.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "nouveau_bo.h"
328c2ecf20Sopenharmony_ci#include "nouveau_connector.h"
338c2ecf20Sopenharmony_ci#include "nouveau_display.h"
348c2ecf20Sopenharmony_ci#include "nouveau_gem.h"
358c2ecf20Sopenharmony_ci#include "nvreg.h"
368c2ecf20Sopenharmony_ci#include "disp.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct nouveau_plane {
398c2ecf20Sopenharmony_ci	struct drm_plane base;
408c2ecf20Sopenharmony_ci	bool flip;
418c2ecf20Sopenharmony_ci	struct nouveau_bo *cur;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	struct {
448c2ecf20Sopenharmony_ci		struct drm_property *colorkey;
458c2ecf20Sopenharmony_ci		struct drm_property *contrast;
468c2ecf20Sopenharmony_ci		struct drm_property *brightness;
478c2ecf20Sopenharmony_ci		struct drm_property *hue;
488c2ecf20Sopenharmony_ci		struct drm_property *saturation;
498c2ecf20Sopenharmony_ci	} props;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	int colorkey;
528c2ecf20Sopenharmony_ci	int contrast;
538c2ecf20Sopenharmony_ci	int brightness;
548c2ecf20Sopenharmony_ci	int hue;
558c2ecf20Sopenharmony_ci	int saturation;
568c2ecf20Sopenharmony_ci	enum drm_color_encoding color_encoding;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	void (*set_params)(struct nouveau_plane *);
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic uint32_t formats[] = {
628c2ecf20Sopenharmony_ci	DRM_FORMAT_YUYV,
638c2ecf20Sopenharmony_ci	DRM_FORMAT_UYVY,
648c2ecf20Sopenharmony_ci	DRM_FORMAT_NV12,
658c2ecf20Sopenharmony_ci	DRM_FORMAT_NV21,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Sine can be approximated with
698c2ecf20Sopenharmony_ci * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula
708c2ecf20Sopenharmony_ci * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) )
718c2ecf20Sopenharmony_ci * Note that this only works for the range [0, 180].
728c2ecf20Sopenharmony_ci * Also note that sin(x) == -sin(x - 180)
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic inline int
758c2ecf20Sopenharmony_cisin_mul(int degrees, int factor)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	if (degrees > 180) {
788c2ecf20Sopenharmony_ci		degrees -= 180;
798c2ecf20Sopenharmony_ci		factor *= -1;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	return factor * 4 * degrees * (180 - degrees) /
828c2ecf20Sopenharmony_ci		(40500 - degrees * (180 - degrees));
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* cos(x) = sin(x + 90) */
868c2ecf20Sopenharmony_cistatic inline int
878c2ecf20Sopenharmony_cicos_mul(int degrees, int factor)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	return sin_mul((degrees + 90) % 360, factor);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int
938c2ecf20Sopenharmony_civerify_scaling(const struct drm_framebuffer *fb, uint8_t shift,
948c2ecf20Sopenharmony_ci               uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
958c2ecf20Sopenharmony_ci               uint32_t crtc_w, uint32_t crtc_h)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	if (crtc_w < (src_w >> shift) || crtc_h < (src_h >> shift)) {
988c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("Unsuitable framebuffer scaling: %dx%d -> %dx%d\n",
998c2ecf20Sopenharmony_ci			      src_w, src_h, crtc_w, crtc_h);
1008c2ecf20Sopenharmony_ci		return -ERANGE;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (src_x != 0 || src_y != 0) {
1048c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("Unsuitable framebuffer offset: %d,%d\n",
1058c2ecf20Sopenharmony_ci                              src_x, src_y);
1068c2ecf20Sopenharmony_ci		return -ERANGE;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int
1138c2ecf20Sopenharmony_cinv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
1148c2ecf20Sopenharmony_ci		  struct drm_framebuffer *fb, int crtc_x, int crtc_y,
1158c2ecf20Sopenharmony_ci		  unsigned int crtc_w, unsigned int crtc_h,
1168c2ecf20Sopenharmony_ci		  uint32_t src_x, uint32_t src_y,
1178c2ecf20Sopenharmony_ci		  uint32_t src_w, uint32_t src_h,
1188c2ecf20Sopenharmony_ci		  struct drm_modeset_acquire_ctx *ctx)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(plane->dev);
1218c2ecf20Sopenharmony_ci	struct nvif_object *dev = &drm->client.device.object;
1228c2ecf20Sopenharmony_ci	struct nouveau_plane *nv_plane =
1238c2ecf20Sopenharmony_ci		container_of(plane, struct nouveau_plane, base);
1248c2ecf20Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
1258c2ecf20Sopenharmony_ci	struct nouveau_bo *cur = nv_plane->cur;
1268c2ecf20Sopenharmony_ci	struct nouveau_bo *nvbo;
1278c2ecf20Sopenharmony_ci	bool flip = nv_plane->flip;
1288c2ecf20Sopenharmony_ci	int soff = NV_PCRTC0_SIZE * nv_crtc->index;
1298c2ecf20Sopenharmony_ci	int soff2 = NV_PCRTC0_SIZE * !nv_crtc->index;
1308c2ecf20Sopenharmony_ci	unsigned shift = drm->client.device.info.chipset >= 0x30 ? 1 : 3;
1318c2ecf20Sopenharmony_ci	unsigned format = 0;
1328c2ecf20Sopenharmony_ci	int ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Source parameters given in 16.16 fixed point, ignore fractional. */
1358c2ecf20Sopenharmony_ci	src_x >>= 16;
1368c2ecf20Sopenharmony_ci	src_y >>= 16;
1378c2ecf20Sopenharmony_ci	src_w >>= 16;
1388c2ecf20Sopenharmony_ci	src_h >>= 16;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ret = verify_scaling(fb, shift, 0, 0, src_w, src_h, crtc_w, crtc_h);
1418c2ecf20Sopenharmony_ci	if (ret)
1428c2ecf20Sopenharmony_ci		return ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	nvbo = nouveau_gem_object(fb->obj[0]);
1458c2ecf20Sopenharmony_ci	ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
1468c2ecf20Sopenharmony_ci	if (ret)
1478c2ecf20Sopenharmony_ci		return ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	nv_plane->cur = nvbo;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY);
1528c2ecf20Sopenharmony_ci	nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0);
1558c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nvbo->offset);
1568c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w);
1578c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x);
1588c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w);
1598c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h);
1608c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x);
1618c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (fb->format->format == DRM_FORMAT_YUYV ||
1648c2ecf20Sopenharmony_ci	    fb->format->format == DRM_FORMAT_NV12)
1658c2ecf20Sopenharmony_ci		format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8;
1668c2ecf20Sopenharmony_ci	if (fb->format->format == DRM_FORMAT_NV12 ||
1678c2ecf20Sopenharmony_ci	    fb->format->format == DRM_FORMAT_NV21)
1688c2ecf20Sopenharmony_ci		format |= NV_PVIDEO_FORMAT_PLANAR;
1698c2ecf20Sopenharmony_ci	if (nv_plane->color_encoding == DRM_COLOR_YCBCR_BT709)
1708c2ecf20Sopenharmony_ci		format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709;
1718c2ecf20Sopenharmony_ci	if (nv_plane->colorkey & (1 << 24))
1728c2ecf20Sopenharmony_ci		format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (format & NV_PVIDEO_FORMAT_PLANAR) {
1758c2ecf20Sopenharmony_ci		nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0);
1768c2ecf20Sopenharmony_ci		nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip),
1778c2ecf20Sopenharmony_ci			nvbo->offset + fb->offsets[1]);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format | fb->pitches[0]);
1808c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_STOP, 0);
1818c2ecf20Sopenharmony_ci	/* TODO: wait for vblank? */
1828c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1);
1838c2ecf20Sopenharmony_ci	nv_plane->flip = !flip;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (cur)
1868c2ecf20Sopenharmony_ci		nouveau_bo_unpin(cur);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int
1928c2ecf20Sopenharmony_cinv10_disable_plane(struct drm_plane *plane,
1938c2ecf20Sopenharmony_ci		   struct drm_modeset_acquire_ctx *ctx)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object;
1968c2ecf20Sopenharmony_ci	struct nouveau_plane *nv_plane =
1978c2ecf20Sopenharmony_ci		container_of(plane, struct nouveau_plane, base);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_STOP, 1);
2008c2ecf20Sopenharmony_ci	if (nv_plane->cur) {
2018c2ecf20Sopenharmony_ci		nouveau_bo_unpin(nv_plane->cur);
2028c2ecf20Sopenharmony_ci		nv_plane->cur = NULL;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void
2098c2ecf20Sopenharmony_cinv_destroy_plane(struct drm_plane *plane)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	drm_plane_force_disable(plane);
2128c2ecf20Sopenharmony_ci	drm_plane_cleanup(plane);
2138c2ecf20Sopenharmony_ci	kfree(plane);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void
2178c2ecf20Sopenharmony_cinv10_set_params(struct nouveau_plane *plane)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct nvif_object *dev = &nouveau_drm(plane->base.dev)->client.device.object;
2208c2ecf20Sopenharmony_ci	u32 luma = (plane->brightness - 512) << 16 | plane->contrast;
2218c2ecf20Sopenharmony_ci	u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) |
2228c2ecf20Sopenharmony_ci		(cos_mul(plane->hue, plane->saturation) & 0xffff);
2238c2ecf20Sopenharmony_ci	u32 format = 0;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma);
2268c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma);
2278c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma);
2288c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma);
2298c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (plane->cur) {
2328c2ecf20Sopenharmony_ci		if (plane->color_encoding == DRM_COLOR_YCBCR_BT709)
2338c2ecf20Sopenharmony_ci			format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709;
2348c2ecf20Sopenharmony_ci		if (plane->colorkey & (1 << 24))
2358c2ecf20Sopenharmony_ci			format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
2368c2ecf20Sopenharmony_ci		nvif_mask(dev, NV_PVIDEO_FORMAT(plane->flip),
2378c2ecf20Sopenharmony_ci			NV_PVIDEO_FORMAT_MATRIX_ITURBT709 |
2388c2ecf20Sopenharmony_ci			NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY,
2398c2ecf20Sopenharmony_ci			format);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int
2448c2ecf20Sopenharmony_cinv_set_property(struct drm_plane *plane,
2458c2ecf20Sopenharmony_ci		struct drm_property *property,
2468c2ecf20Sopenharmony_ci		uint64_t value)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct nouveau_plane *nv_plane =
2498c2ecf20Sopenharmony_ci		container_of(plane, struct nouveau_plane, base);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (property == nv_plane->props.colorkey)
2528c2ecf20Sopenharmony_ci		nv_plane->colorkey = value;
2538c2ecf20Sopenharmony_ci	else if (property == nv_plane->props.contrast)
2548c2ecf20Sopenharmony_ci		nv_plane->contrast = value;
2558c2ecf20Sopenharmony_ci	else if (property == nv_plane->props.brightness)
2568c2ecf20Sopenharmony_ci		nv_plane->brightness = value;
2578c2ecf20Sopenharmony_ci	else if (property == nv_plane->props.hue)
2588c2ecf20Sopenharmony_ci		nv_plane->hue = value;
2598c2ecf20Sopenharmony_ci	else if (property == nv_plane->props.saturation)
2608c2ecf20Sopenharmony_ci		nv_plane->saturation = value;
2618c2ecf20Sopenharmony_ci	else if (property == nv_plane->base.color_encoding_property)
2628c2ecf20Sopenharmony_ci		nv_plane->color_encoding = value;
2638c2ecf20Sopenharmony_ci	else
2648c2ecf20Sopenharmony_ci		return -EINVAL;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (nv_plane->set_params)
2678c2ecf20Sopenharmony_ci		nv_plane->set_params(nv_plane);
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs nv10_plane_funcs = {
2728c2ecf20Sopenharmony_ci	.update_plane = nv10_update_plane,
2738c2ecf20Sopenharmony_ci	.disable_plane = nv10_disable_plane,
2748c2ecf20Sopenharmony_ci	.set_property = nv_set_property,
2758c2ecf20Sopenharmony_ci	.destroy = nv_destroy_plane,
2768c2ecf20Sopenharmony_ci};
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void
2798c2ecf20Sopenharmony_cinv10_overlay_init(struct drm_device *device)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(device);
2828c2ecf20Sopenharmony_ci	struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL);
2838c2ecf20Sopenharmony_ci	unsigned int num_formats = ARRAY_SIZE(formats);
2848c2ecf20Sopenharmony_ci	int ret;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (!plane)
2878c2ecf20Sopenharmony_ci		return;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	switch (drm->client.device.info.chipset) {
2908c2ecf20Sopenharmony_ci	case 0x10:
2918c2ecf20Sopenharmony_ci	case 0x11:
2928c2ecf20Sopenharmony_ci	case 0x15:
2938c2ecf20Sopenharmony_ci	case 0x1a:
2948c2ecf20Sopenharmony_ci	case 0x20:
2958c2ecf20Sopenharmony_ci		num_formats = 2;
2968c2ecf20Sopenharmony_ci		break;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */,
3008c2ecf20Sopenharmony_ci			     &nv10_plane_funcs,
3018c2ecf20Sopenharmony_ci			     formats, num_formats, false);
3028c2ecf20Sopenharmony_ci	if (ret)
3038c2ecf20Sopenharmony_ci		goto err;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* Set up the plane properties */
3068c2ecf20Sopenharmony_ci	plane->props.colorkey = drm_property_create_range(
3078c2ecf20Sopenharmony_ci			device, 0, "colorkey", 0, 0x01ffffff);
3088c2ecf20Sopenharmony_ci	plane->props.contrast = drm_property_create_range(
3098c2ecf20Sopenharmony_ci			device, 0, "contrast", 0, 8192 - 1);
3108c2ecf20Sopenharmony_ci	plane->props.brightness = drm_property_create_range(
3118c2ecf20Sopenharmony_ci			device, 0, "brightness", 0, 1024);
3128c2ecf20Sopenharmony_ci	plane->props.hue = drm_property_create_range(
3138c2ecf20Sopenharmony_ci			device, 0, "hue", 0, 359);
3148c2ecf20Sopenharmony_ci	plane->props.saturation = drm_property_create_range(
3158c2ecf20Sopenharmony_ci			device, 0, "saturation", 0, 8192 - 1);
3168c2ecf20Sopenharmony_ci	if (!plane->props.colorkey ||
3178c2ecf20Sopenharmony_ci	    !plane->props.contrast ||
3188c2ecf20Sopenharmony_ci	    !plane->props.brightness ||
3198c2ecf20Sopenharmony_ci	    !plane->props.hue ||
3208c2ecf20Sopenharmony_ci	    !plane->props.saturation)
3218c2ecf20Sopenharmony_ci		goto cleanup;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	plane->colorkey = 0;
3248c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
3258c2ecf20Sopenharmony_ci				   plane->props.colorkey, plane->colorkey);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	plane->contrast = 0x1000;
3288c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
3298c2ecf20Sopenharmony_ci				   plane->props.contrast, plane->contrast);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	plane->brightness = 512;
3328c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
3338c2ecf20Sopenharmony_ci				   plane->props.brightness, plane->brightness);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	plane->hue = 0;
3368c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
3378c2ecf20Sopenharmony_ci				   plane->props.hue, plane->hue);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	plane->saturation = 0x1000;
3408c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
3418c2ecf20Sopenharmony_ci				   plane->props.saturation, plane->saturation);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	plane->color_encoding = DRM_COLOR_YCBCR_BT601;
3448c2ecf20Sopenharmony_ci	drm_plane_create_color_properties(&plane->base,
3458c2ecf20Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_BT601) |
3468c2ecf20Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_BT709),
3478c2ecf20Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
3488c2ecf20Sopenharmony_ci					  DRM_COLOR_YCBCR_BT601,
3498c2ecf20Sopenharmony_ci					  DRM_COLOR_YCBCR_LIMITED_RANGE);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	plane->set_params = nv10_set_params;
3528c2ecf20Sopenharmony_ci	nv10_set_params(plane);
3538c2ecf20Sopenharmony_ci	drm_plane_force_disable(&plane->base);
3548c2ecf20Sopenharmony_ci	return;
3558c2ecf20Sopenharmony_cicleanup:
3568c2ecf20Sopenharmony_ci	drm_plane_cleanup(&plane->base);
3578c2ecf20Sopenharmony_cierr:
3588c2ecf20Sopenharmony_ci	kfree(plane);
3598c2ecf20Sopenharmony_ci	NV_ERROR(drm, "Failed to create plane\n");
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int
3638c2ecf20Sopenharmony_cinv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
3648c2ecf20Sopenharmony_ci		  struct drm_framebuffer *fb, int crtc_x, int crtc_y,
3658c2ecf20Sopenharmony_ci		  unsigned int crtc_w, unsigned int crtc_h,
3668c2ecf20Sopenharmony_ci		  uint32_t src_x, uint32_t src_y,
3678c2ecf20Sopenharmony_ci		  uint32_t src_w, uint32_t src_h,
3688c2ecf20Sopenharmony_ci		  struct drm_modeset_acquire_ctx *ctx)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object;
3718c2ecf20Sopenharmony_ci	struct nouveau_plane *nv_plane =
3728c2ecf20Sopenharmony_ci		container_of(plane, struct nouveau_plane, base);
3738c2ecf20Sopenharmony_ci	struct nouveau_bo *cur = nv_plane->cur;
3748c2ecf20Sopenharmony_ci	struct nouveau_bo *nvbo;
3758c2ecf20Sopenharmony_ci	uint32_t overlay = 1;
3768c2ecf20Sopenharmony_ci	int brightness = (nv_plane->brightness - 512) * 62 / 512;
3778c2ecf20Sopenharmony_ci	int ret, i;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* Source parameters given in 16.16 fixed point, ignore fractional. */
3808c2ecf20Sopenharmony_ci	src_x >>= 16;
3818c2ecf20Sopenharmony_ci	src_y >>= 16;
3828c2ecf20Sopenharmony_ci	src_w >>= 16;
3838c2ecf20Sopenharmony_ci	src_h >>= 16;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = verify_scaling(fb, 0, src_x, src_y, src_w, src_h, crtc_w, crtc_h);
3868c2ecf20Sopenharmony_ci	if (ret)
3878c2ecf20Sopenharmony_ci		return ret;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	nvbo = nouveau_gem_object(fb->obj[0]);
3908c2ecf20Sopenharmony_ci	ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
3918c2ecf20Sopenharmony_ci	if (ret)
3928c2ecf20Sopenharmony_ci		return ret;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	nv_plane->cur = nvbo;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0);
3978c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0);
3988c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
4018c2ecf20Sopenharmony_ci		nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i,
4028c2ecf20Sopenharmony_ci			  nvbo->offset);
4038c2ecf20Sopenharmony_ci		nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i,
4048c2ecf20Sopenharmony_ci			  fb->pitches[0]);
4058c2ecf20Sopenharmony_ci		nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x);
4088c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w);
4098c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_STEP_SIZE,
4108c2ecf20Sopenharmony_ci		(uint32_t)(((src_h - 1) << 11) / (crtc_h - 1)) << 16 | (uint32_t)(((src_w - 1) << 11) / (crtc_w - 1)));
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* It should be possible to convert hue/contrast to this */
4138c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness);
4148c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness);
4158c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness);
4168c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */
4198c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03);
4228c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (nv_plane->colorkey & (1 << 24))
4278c2ecf20Sopenharmony_ci		overlay |= 0x10;
4288c2ecf20Sopenharmony_ci	if (fb->format->format == DRM_FORMAT_YUYV)
4298c2ecf20Sopenharmony_ci		overlay |= 0x100;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16));
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (cur)
4368c2ecf20Sopenharmony_ci		nouveau_bo_unpin(cur);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic int
4428c2ecf20Sopenharmony_cinv04_disable_plane(struct drm_plane *plane,
4438c2ecf20Sopenharmony_ci		   struct drm_modeset_acquire_ctx *ctx)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object;
4468c2ecf20Sopenharmony_ci	struct nouveau_plane *nv_plane =
4478c2ecf20Sopenharmony_ci		container_of(plane, struct nouveau_plane, base);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0);
4508c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0);
4518c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0);
4528c2ecf20Sopenharmony_ci	nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0);
4538c2ecf20Sopenharmony_ci	if (nv_plane->cur) {
4548c2ecf20Sopenharmony_ci		nouveau_bo_unpin(nv_plane->cur);
4558c2ecf20Sopenharmony_ci		nv_plane->cur = NULL;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs nv04_plane_funcs = {
4628c2ecf20Sopenharmony_ci	.update_plane = nv04_update_plane,
4638c2ecf20Sopenharmony_ci	.disable_plane = nv04_disable_plane,
4648c2ecf20Sopenharmony_ci	.set_property = nv_set_property,
4658c2ecf20Sopenharmony_ci	.destroy = nv_destroy_plane,
4668c2ecf20Sopenharmony_ci};
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void
4698c2ecf20Sopenharmony_cinv04_overlay_init(struct drm_device *device)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(device);
4728c2ecf20Sopenharmony_ci	struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL);
4738c2ecf20Sopenharmony_ci	int ret;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (!plane)
4768c2ecf20Sopenharmony_ci		return;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	ret = drm_plane_init(device, &plane->base, 1 /* single crtc */,
4798c2ecf20Sopenharmony_ci			     &nv04_plane_funcs,
4808c2ecf20Sopenharmony_ci			     formats, 2, false);
4818c2ecf20Sopenharmony_ci	if (ret)
4828c2ecf20Sopenharmony_ci		goto err;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* Set up the plane properties */
4858c2ecf20Sopenharmony_ci	plane->props.colorkey = drm_property_create_range(
4868c2ecf20Sopenharmony_ci			device, 0, "colorkey", 0, 0x01ffffff);
4878c2ecf20Sopenharmony_ci	plane->props.brightness = drm_property_create_range(
4888c2ecf20Sopenharmony_ci			device, 0, "brightness", 0, 1024);
4898c2ecf20Sopenharmony_ci	if (!plane->props.colorkey ||
4908c2ecf20Sopenharmony_ci	    !plane->props.brightness)
4918c2ecf20Sopenharmony_ci		goto cleanup;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	plane->colorkey = 0;
4948c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
4958c2ecf20Sopenharmony_ci				   plane->props.colorkey, plane->colorkey);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	plane->brightness = 512;
4988c2ecf20Sopenharmony_ci	drm_object_attach_property(&plane->base.base,
4998c2ecf20Sopenharmony_ci				   plane->props.brightness, plane->brightness);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	drm_plane_force_disable(&plane->base);
5028c2ecf20Sopenharmony_ci	return;
5038c2ecf20Sopenharmony_cicleanup:
5048c2ecf20Sopenharmony_ci	drm_plane_cleanup(&plane->base);
5058c2ecf20Sopenharmony_cierr:
5068c2ecf20Sopenharmony_ci	kfree(plane);
5078c2ecf20Sopenharmony_ci	NV_ERROR(drm, "Failed to create plane\n");
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_civoid
5118c2ecf20Sopenharmony_cinouveau_overlay_init(struct drm_device *device)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct nvif_device *dev = &nouveau_drm(device)->client.device;
5148c2ecf20Sopenharmony_ci	if (dev->info.chipset < 0x10)
5158c2ecf20Sopenharmony_ci		nv04_overlay_init(device);
5168c2ecf20Sopenharmony_ci	else if (dev->info.chipset <= 0x40)
5178c2ecf20Sopenharmony_ci		nv10_overlay_init(device);
5188c2ecf20Sopenharmony_ci}
519