cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nouveau_backlight.c (12376B)


      1/*
      2 * Copyright (C) 2009 Red Hat <mjg@redhat.com>
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining
      5 * a copy of this software and associated documentation files (the
      6 * "Software"), to deal in the Software without restriction, including
      7 * without limitation the rights to use, copy, modify, merge, publish,
      8 * distribute, sublicense, and/or sell copies of the Software, and to
      9 * permit persons to whom the Software is furnished to do so, subject to
     10 * the following conditions:
     11 *
     12 * The above copyright notice and this permission notice (including the
     13 * next paragraph) shall be included in all copies or substantial
     14 * portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     19 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
     20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 *
     24 */
     25
     26/*
     27 * Authors:
     28 *  Matthew Garrett <mjg@redhat.com>
     29 *
     30 * Register locations derived from NVClock by Roderick Colenbrander
     31 */
     32
     33#include <linux/apple-gmux.h>
     34#include <linux/backlight.h>
     35#include <linux/idr.h>
     36
     37#include "nouveau_drv.h"
     38#include "nouveau_reg.h"
     39#include "nouveau_encoder.h"
     40#include "nouveau_connector.h"
     41
     42static struct ida bl_ida;
     43#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
     44
     45static bool
     46nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
     47			   struct nouveau_backlight *bl)
     48{
     49	const int nb = ida_alloc_max(&bl_ida, 99, GFP_KERNEL);
     50
     51	if (nb < 0)
     52		return false;
     53	if (nb > 0)
     54		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
     55	else
     56		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
     57	bl->id = nb;
     58	return true;
     59}
     60
     61static int
     62nv40_get_intensity(struct backlight_device *bd)
     63{
     64	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
     65	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
     66	struct nvif_object *device = &drm->client.device.object;
     67	int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
     68		   NV40_PMC_BACKLIGHT_MASK) >> 16;
     69
     70	return val;
     71}
     72
     73static int
     74nv40_set_intensity(struct backlight_device *bd)
     75{
     76	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
     77	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
     78	struct nvif_object *device = &drm->client.device.object;
     79	int val = bd->props.brightness;
     80	int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
     81
     82	nvif_wr32(device, NV40_PMC_BACKLIGHT,
     83		  (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
     84
     85	return 0;
     86}
     87
     88static const struct backlight_ops nv40_bl_ops = {
     89	.options = BL_CORE_SUSPENDRESUME,
     90	.get_brightness = nv40_get_intensity,
     91	.update_status = nv40_set_intensity,
     92};
     93
     94static int
     95nv40_backlight_init(struct nouveau_encoder *encoder,
     96		    struct backlight_properties *props,
     97		    const struct backlight_ops **ops)
     98{
     99	struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);
    100	struct nvif_object *device = &drm->client.device.object;
    101
    102	if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
    103		return -ENODEV;
    104
    105	props->max_brightness = 31;
    106	*ops = &nv40_bl_ops;
    107	return 0;
    108}
    109
    110static int
    111nv50_get_intensity(struct backlight_device *bd)
    112{
    113	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    114	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    115	struct nvif_object *device = &drm->client.device.object;
    116	int or = ffs(nv_encoder->dcb->or) - 1;
    117	u32 div = 1025;
    118	u32 val;
    119
    120	val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
    121	val &= NV50_PDISP_SOR_PWM_CTL_VAL;
    122	return ((val * 100) + (div / 2)) / div;
    123}
    124
    125static int
    126nv50_set_intensity(struct backlight_device *bd)
    127{
    128	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    129	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    130	struct nvif_object *device = &drm->client.device.object;
    131	int or = ffs(nv_encoder->dcb->or) - 1;
    132	u32 div = 1025;
    133	u32 val = (bd->props.brightness * div) / 100;
    134
    135	nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
    136		  NV50_PDISP_SOR_PWM_CTL_NEW | val);
    137	return 0;
    138}
    139
    140static const struct backlight_ops nv50_bl_ops = {
    141	.options = BL_CORE_SUSPENDRESUME,
    142	.get_brightness = nv50_get_intensity,
    143	.update_status = nv50_set_intensity,
    144};
    145
    146/*
    147 * eDP brightness callbacks need to happen under lock, since we need to
    148 * enable/disable the backlight ourselves for modesets
    149 */
    150static int
    151nv50_edp_get_brightness(struct backlight_device *bd)
    152{
    153	struct drm_connector *connector = dev_get_drvdata(bd->dev.parent);
    154	struct drm_device *dev = connector->dev;
    155	struct drm_crtc *crtc;
    156	struct drm_modeset_acquire_ctx ctx;
    157	int ret = 0;
    158
    159	drm_modeset_acquire_init(&ctx, 0);
    160
    161retry:
    162	ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
    163	if (ret == -EDEADLK)
    164		goto deadlock;
    165	else if (ret < 0)
    166		goto out;
    167
    168	crtc = connector->state->crtc;
    169	if (!crtc)
    170		goto out;
    171
    172	ret = drm_modeset_lock(&crtc->mutex, &ctx);
    173	if (ret == -EDEADLK)
    174		goto deadlock;
    175	else if (ret < 0)
    176		goto out;
    177
    178	if (!crtc->state->active)
    179		goto out;
    180
    181	ret = bd->props.brightness;
    182out:
    183	drm_modeset_drop_locks(&ctx);
    184	drm_modeset_acquire_fini(&ctx);
    185	return ret;
    186deadlock:
    187	drm_modeset_backoff(&ctx);
    188	goto retry;
    189}
    190
    191static int
    192nv50_edp_set_brightness(struct backlight_device *bd)
    193{
    194	struct drm_connector *connector = dev_get_drvdata(bd->dev.parent);
    195	struct nouveau_connector *nv_connector = nouveau_connector(connector);
    196	struct drm_device *dev = connector->dev;
    197	struct drm_crtc *crtc;
    198	struct drm_dp_aux *aux = &nv_connector->aux;
    199	struct nouveau_backlight *nv_bl = nv_connector->backlight;
    200	struct drm_modeset_acquire_ctx ctx;
    201	int ret = 0;
    202
    203	drm_modeset_acquire_init(&ctx, 0);
    204retry:
    205	ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
    206	if (ret == -EDEADLK)
    207		goto deadlock;
    208	else if (ret < 0)
    209		goto out;
    210
    211	crtc = connector->state->crtc;
    212	if (!crtc)
    213		goto out;
    214
    215	ret = drm_modeset_lock(&crtc->mutex, &ctx);
    216	if (ret == -EDEADLK)
    217		goto deadlock;
    218	else if (ret < 0)
    219		goto out;
    220
    221	if (crtc->state->active)
    222		ret = drm_edp_backlight_set_level(aux, &nv_bl->edp_info, bd->props.brightness);
    223
    224out:
    225	drm_modeset_drop_locks(&ctx);
    226	drm_modeset_acquire_fini(&ctx);
    227	return ret;
    228deadlock:
    229	drm_modeset_backoff(&ctx);
    230	goto retry;
    231}
    232
    233static const struct backlight_ops nv50_edp_bl_ops = {
    234	.get_brightness = nv50_edp_get_brightness,
    235	.update_status = nv50_edp_set_brightness,
    236};
    237
    238static int
    239nva3_get_intensity(struct backlight_device *bd)
    240{
    241	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    242	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    243	struct nvif_object *device = &drm->client.device.object;
    244	int or = ffs(nv_encoder->dcb->or) - 1;
    245	u32 div, val;
    246
    247	div  = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
    248	val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
    249	val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
    250	if (div && div >= val)
    251		return ((val * 100) + (div / 2)) / div;
    252
    253	return 100;
    254}
    255
    256static int
    257nva3_set_intensity(struct backlight_device *bd)
    258{
    259	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    260	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    261	struct nvif_object *device = &drm->client.device.object;
    262	int or = ffs(nv_encoder->dcb->or) - 1;
    263	u32 div, val;
    264
    265	div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
    266	val = (bd->props.brightness * div) / 100;
    267	if (div) {
    268		nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
    269			  val |
    270			  NV50_PDISP_SOR_PWM_CTL_NEW |
    271			  NVA3_PDISP_SOR_PWM_CTL_UNK);
    272		return 0;
    273	}
    274
    275	return -EINVAL;
    276}
    277
    278static const struct backlight_ops nva3_bl_ops = {
    279	.options = BL_CORE_SUSPENDRESUME,
    280	.get_brightness = nva3_get_intensity,
    281	.update_status = nva3_set_intensity,
    282};
    283
    284/* FIXME: perform backlight probing for eDP _before_ this, this only gets called after connector
    285 * registration which happens after the initial modeset
    286 */
    287static int
    288nv50_backlight_init(struct nouveau_backlight *bl,
    289		    struct nouveau_connector *nv_conn,
    290		    struct nouveau_encoder *nv_encoder,
    291		    struct backlight_properties *props,
    292		    const struct backlight_ops **ops)
    293{
    294	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    295	struct nvif_object *device = &drm->client.device.object;
    296
    297	if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)) ||
    298	    nv_conn->base.status != connector_status_connected)
    299		return -ENODEV;
    300
    301	if (nv_conn->type == DCB_CONNECTOR_eDP) {
    302		int ret;
    303		u16 current_level;
    304		u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
    305		u8 current_mode;
    306
    307		ret = drm_dp_dpcd_read(&nv_conn->aux, DP_EDP_DPCD_REV, edp_dpcd,
    308				       EDP_DISPLAY_CTL_CAP_SIZE);
    309		if (ret < 0)
    310			return ret;
    311
    312		/* TODO: Add support for hybrid PWM/DPCD panels */
    313		if (drm_edp_backlight_supported(edp_dpcd) &&
    314		    (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
    315		    (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {
    316			NV_DEBUG(drm, "DPCD backlight controls supported on %s\n",
    317				 nv_conn->base.name);
    318
    319			ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info, 0, edp_dpcd,
    320						     &current_level, &current_mode);
    321			if (ret < 0)
    322				return ret;
    323
    324			ret = drm_edp_backlight_enable(&nv_conn->aux, &bl->edp_info, current_level);
    325			if (ret < 0) {
    326				NV_ERROR(drm, "Failed to enable backlight on %s: %d\n",
    327					 nv_conn->base.name, ret);
    328				return ret;
    329			}
    330
    331			*ops = &nv50_edp_bl_ops;
    332			props->brightness = current_level;
    333			props->max_brightness = bl->edp_info.max;
    334			bl->uses_dpcd = true;
    335			return 0;
    336		}
    337	}
    338
    339	if (drm->client.device.info.chipset <= 0xa0 ||
    340	    drm->client.device.info.chipset == 0xaa ||
    341	    drm->client.device.info.chipset == 0xac)
    342		*ops = &nv50_bl_ops;
    343	else
    344		*ops = &nva3_bl_ops;
    345
    346	props->max_brightness = 100;
    347
    348	return 0;
    349}
    350
    351int
    352nouveau_backlight_init(struct drm_connector *connector)
    353{
    354	struct nouveau_drm *drm = nouveau_drm(connector->dev);
    355	struct nouveau_backlight *bl;
    356	struct nouveau_encoder *nv_encoder = NULL;
    357	struct nvif_device *device = &drm->client.device;
    358	char backlight_name[BL_NAME_SIZE];
    359	struct backlight_properties props = {0};
    360	const struct backlight_ops *ops;
    361	int ret;
    362
    363	if (apple_gmux_present()) {
    364		NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
    365		return 0;
    366	}
    367
    368	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
    369		nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
    370	else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
    371		nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
    372	else
    373		return 0;
    374
    375	if (!nv_encoder)
    376		return 0;
    377
    378	bl = kzalloc(sizeof(*bl), GFP_KERNEL);
    379	if (!bl)
    380		return -ENOMEM;
    381
    382	switch (device->info.family) {
    383	case NV_DEVICE_INFO_V0_CURIE:
    384		ret = nv40_backlight_init(nv_encoder, &props, &ops);
    385		break;
    386	case NV_DEVICE_INFO_V0_TESLA:
    387	case NV_DEVICE_INFO_V0_FERMI:
    388	case NV_DEVICE_INFO_V0_KEPLER:
    389	case NV_DEVICE_INFO_V0_MAXWELL:
    390	case NV_DEVICE_INFO_V0_PASCAL:
    391	case NV_DEVICE_INFO_V0_VOLTA:
    392	case NV_DEVICE_INFO_V0_TURING:
    393	case NV_DEVICE_INFO_V0_AMPERE: //XXX: not confirmed
    394		ret = nv50_backlight_init(bl, nouveau_connector(connector),
    395					  nv_encoder, &props, &ops);
    396		break;
    397	default:
    398		ret = 0;
    399		goto fail_alloc;
    400	}
    401
    402	if (ret) {
    403		if (ret == -ENODEV)
    404			ret = 0;
    405		goto fail_alloc;
    406	}
    407
    408	if (!nouveau_get_backlight_name(backlight_name, bl)) {
    409		NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
    410		goto fail_alloc;
    411	}
    412
    413	props.type = BACKLIGHT_RAW;
    414	bl->dev = backlight_device_register(backlight_name, connector->kdev,
    415					    nv_encoder, ops, &props);
    416	if (IS_ERR(bl->dev)) {
    417		if (bl->id >= 0)
    418			ida_free(&bl_ida, bl->id);
    419		ret = PTR_ERR(bl->dev);
    420		goto fail_alloc;
    421	}
    422
    423	nouveau_connector(connector)->backlight = bl;
    424	if (!bl->dev->props.brightness)
    425		bl->dev->props.brightness =
    426			bl->dev->ops->get_brightness(bl->dev);
    427	backlight_update_status(bl->dev);
    428
    429	return 0;
    430
    431fail_alloc:
    432	kfree(bl);
    433	return ret;
    434}
    435
    436void
    437nouveau_backlight_fini(struct drm_connector *connector)
    438{
    439	struct nouveau_connector *nv_conn = nouveau_connector(connector);
    440	struct nouveau_backlight *bl = nv_conn->backlight;
    441
    442	if (!bl)
    443		return;
    444
    445	if (bl->id >= 0)
    446		ida_free(&bl_ida, bl->id);
    447
    448	backlight_device_unregister(bl->dev);
    449	nv_conn->backlight = NULL;
    450	kfree(bl);
    451}
    452
    453void
    454nouveau_backlight_ctor(void)
    455{
    456	ida_init(&bl_ida);
    457}
    458
    459void
    460nouveau_backlight_dtor(void)
    461{
    462	ida_destroy(&bl_ida);
    463}