162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Backlight code for nVidia based graphic cards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2004 Antonino Daplas <adaplas@pol.net>
662306a36Sopenharmony_ci * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/backlight.h>
1062306a36Sopenharmony_ci#include <linux/fb.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
1462306a36Sopenharmony_ci#include <asm/backlight.h>
1562306a36Sopenharmony_ci#endif
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "nv_local.h"
1862306a36Sopenharmony_ci#include "nv_type.h"
1962306a36Sopenharmony_ci#include "nv_proto.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* We do not have any information about which values are allowed, thus
2262306a36Sopenharmony_ci * we used safe values.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#define MIN_LEVEL 0x158
2562306a36Sopenharmony_ci#define MAX_LEVEL 0x534
2662306a36Sopenharmony_ci#define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int nvidia_bl_get_level_brightness(struct nvidia_par *par,
2962306a36Sopenharmony_ci		int level)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pci_dev);
3262306a36Sopenharmony_ci	int nlevel;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Get and convert the value */
3562306a36Sopenharmony_ci	/* No locking of bl_curve since we read a single value */
3662306a36Sopenharmony_ci	nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (nlevel < 0)
3962306a36Sopenharmony_ci		nlevel = 0;
4062306a36Sopenharmony_ci	else if (nlevel < MIN_LEVEL)
4162306a36Sopenharmony_ci		nlevel = MIN_LEVEL;
4262306a36Sopenharmony_ci	else if (nlevel > MAX_LEVEL)
4362306a36Sopenharmony_ci		nlevel = MAX_LEVEL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return nlevel;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int nvidia_bl_update_status(struct backlight_device *bd)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct nvidia_par *par = bl_get_data(bd);
5162306a36Sopenharmony_ci	u32 tmp_pcrt, tmp_pmc, fpcontrol;
5262306a36Sopenharmony_ci	int level = backlight_get_brightness(bd);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!par->FlatPanel)
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
5862306a36Sopenharmony_ci	tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
5962306a36Sopenharmony_ci	fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (level > 0) {
6262306a36Sopenharmony_ci		tmp_pcrt |= 0x1;
6362306a36Sopenharmony_ci		tmp_pmc |= (1 << 31); /* backlight bit */
6462306a36Sopenharmony_ci		tmp_pmc |= nvidia_bl_get_level_brightness(par, level) << 16;
6562306a36Sopenharmony_ci		fpcontrol |= par->fpSyncs;
6662306a36Sopenharmony_ci	} else
6762306a36Sopenharmony_ci		fpcontrol |= 0x20000022;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
7062306a36Sopenharmony_ci	NV_WR32(par->PMC, 0x10F0, tmp_pmc);
7162306a36Sopenharmony_ci	NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct backlight_ops nvidia_bl_ops = {
7762306a36Sopenharmony_ci	.update_status	= nvidia_bl_update_status,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid nvidia_bl_init(struct nvidia_par *par)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct backlight_properties props;
8362306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pci_dev);
8462306a36Sopenharmony_ci	struct backlight_device *bd;
8562306a36Sopenharmony_ci	char name[12];
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (!par->FlatPanel)
8862306a36Sopenharmony_ci		return;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
9162306a36Sopenharmony_ci	if (!machine_is(powermac) ||
9262306a36Sopenharmony_ci	    !pmac_has_backlight_type("mnca"))
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	snprintf(name, sizeof(name), "nvidiabl%d", info->node);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
9962306a36Sopenharmony_ci	props.type = BACKLIGHT_RAW;
10062306a36Sopenharmony_ci	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
10162306a36Sopenharmony_ci	bd = backlight_device_register(name, info->device, par, &nvidia_bl_ops,
10262306a36Sopenharmony_ci				       &props);
10362306a36Sopenharmony_ci	if (IS_ERR(bd)) {
10462306a36Sopenharmony_ci		info->bl_dev = NULL;
10562306a36Sopenharmony_ci		printk(KERN_WARNING "nvidia: Backlight registration failed\n");
10662306a36Sopenharmony_ci		goto error;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	info->bl_dev = bd;
11062306a36Sopenharmony_ci	fb_bl_default_curve(info, 0,
11162306a36Sopenharmony_ci		0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
11262306a36Sopenharmony_ci		0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	bd->props.brightness = bd->props.max_brightness;
11562306a36Sopenharmony_ci	bd->props.power = FB_BLANK_UNBLANK;
11662306a36Sopenharmony_ci	backlight_update_status(bd);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	printk("nvidia: Backlight initialized (%s)\n", name);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cierror:
12162306a36Sopenharmony_ci	return;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_civoid nvidia_bl_exit(struct nvidia_par *par)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pci_dev);
12762306a36Sopenharmony_ci	struct backlight_device *bd = info->bl_dev;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	backlight_device_unregister(bd);
13062306a36Sopenharmony_ci	printk("nvidia: Backlight unloaded\n");
13162306a36Sopenharmony_ci}
132