brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · d474421 Raw
414 lines · c
1/*2 * Copyright (C) 2009 Red Hat <mjg@redhat.com>3 *4 * Permission is hereby granted, free of charge, to any person obtaining5 * a copy of this software and associated documentation files (the6 * "Software"), to deal in the Software without restriction, including7 * without limitation the rights to use, copy, modify, merge, publish,8 * distribute, sublicense, and/or sell copies of the Software, and to9 * permit persons to whom the Software is furnished to do so, subject to10 * the following conditions:11 *12 * The above copyright notice and this permission notice (including the13 * next paragraph) shall be included in all copies or substantial14 * 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 OF18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22 * 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 Colenbrander31 */32 33#include <linux/apple-gmux.h>34#include <linux/backlight.h>35#include <linux/idr.h>36#include <drm/drm_probe_helper.h>37 38#include "nouveau_drv.h"39#include "nouveau_reg.h"40#include "nouveau_encoder.h"41#include "nouveau_connector.h"42#include "nouveau_acpi.h"43 44static struct ida bl_ida;45#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'46 47static bool48nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],49			   struct nouveau_backlight *bl)50{51	const int nb = ida_alloc_max(&bl_ida, 99, GFP_KERNEL);52 53	if (nb < 0)54		return false;55	if (nb > 0)56		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);57	else58		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");59	bl->id = nb;60	return true;61}62 63static int64nv40_get_intensity(struct backlight_device *bd)65{66	struct nouveau_encoder *nv_encoder = bl_get_data(bd);67	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);68	struct nvif_object *device = &drm->client.device.object;69	int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &70		   NV40_PMC_BACKLIGHT_MASK) >> 16;71 72	return val;73}74 75static int76nv40_set_intensity(struct backlight_device *bd)77{78	struct nouveau_encoder *nv_encoder = bl_get_data(bd);79	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);80	struct nvif_object *device = &drm->client.device.object;81	int val = bd->props.brightness;82	int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);83 84	nvif_wr32(device, NV40_PMC_BACKLIGHT,85		  (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));86 87	return 0;88}89 90static const struct backlight_ops nv40_bl_ops = {91	.options = BL_CORE_SUSPENDRESUME,92	.get_brightness = nv40_get_intensity,93	.update_status = nv40_set_intensity,94};95 96static int97nv40_backlight_init(struct nouveau_encoder *encoder,98		    struct backlight_properties *props,99		    const struct backlight_ops **ops)100{101	struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);102	struct nvif_object *device = &drm->client.device.object;103 104	if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))105		return -ENODEV;106 107	props->max_brightness = 31;108	*ops = &nv40_bl_ops;109	return 0;110}111 112/*113 * eDP brightness callbacks need to happen under lock, since we need to114 * enable/disable the backlight ourselves for modesets115 */116static int117nv50_edp_get_brightness(struct backlight_device *bd)118{119	struct drm_connector *connector = dev_get_drvdata(bd->dev.parent);120	struct drm_device *dev = connector->dev;121	struct drm_crtc *crtc;122	struct drm_modeset_acquire_ctx ctx;123	int ret = 0;124 125	drm_modeset_acquire_init(&ctx, 0);126 127retry:128	ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);129	if (ret == -EDEADLK)130		goto deadlock;131	else if (ret < 0)132		goto out;133 134	crtc = connector->state->crtc;135	if (!crtc)136		goto out;137 138	ret = drm_modeset_lock(&crtc->mutex, &ctx);139	if (ret == -EDEADLK)140		goto deadlock;141	else if (ret < 0)142		goto out;143 144	if (!crtc->state->active)145		goto out;146 147	ret = bd->props.brightness;148out:149	drm_modeset_drop_locks(&ctx);150	drm_modeset_acquire_fini(&ctx);151	return ret;152deadlock:153	drm_modeset_backoff(&ctx);154	goto retry;155}156 157static int158nv50_edp_set_brightness(struct backlight_device *bd)159{160	struct drm_connector *connector = dev_get_drvdata(bd->dev.parent);161	struct nouveau_connector *nv_connector = nouveau_connector(connector);162	struct drm_device *dev = connector->dev;163	struct drm_crtc *crtc;164	struct drm_dp_aux *aux = &nv_connector->aux;165	struct nouveau_backlight *nv_bl = nv_connector->backlight;166	struct drm_modeset_acquire_ctx ctx;167	int ret = 0;168 169	drm_modeset_acquire_init(&ctx, 0);170retry:171	ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);172	if (ret == -EDEADLK)173		goto deadlock;174	else if (ret < 0)175		goto out;176 177	crtc = connector->state->crtc;178	if (!crtc)179		goto out;180 181	ret = drm_modeset_lock(&crtc->mutex, &ctx);182	if (ret == -EDEADLK)183		goto deadlock;184	else if (ret < 0)185		goto out;186 187	if (crtc->state->active)188		ret = drm_edp_backlight_set_level(aux, &nv_bl->edp_info, bd->props.brightness);189 190out:191	drm_modeset_drop_locks(&ctx);192	drm_modeset_acquire_fini(&ctx);193	return ret;194deadlock:195	drm_modeset_backoff(&ctx);196	goto retry;197}198 199static const struct backlight_ops nv50_edp_bl_ops = {200	.get_brightness = nv50_edp_get_brightness,201	.update_status = nv50_edp_set_brightness,202};203 204static int205nv50_get_intensity(struct backlight_device *bd)206{207	struct nouveau_encoder *nv_encoder = bl_get_data(bd);208 209	return nvif_outp_bl_get(&nv_encoder->outp);210}211 212static int213nv50_set_intensity(struct backlight_device *bd)214{215	struct nouveau_encoder *nv_encoder = bl_get_data(bd);216 217	return nvif_outp_bl_set(&nv_encoder->outp, backlight_get_brightness(bd));218}219 220static const struct backlight_ops nv50_bl_ops = {221	.options = BL_CORE_SUSPENDRESUME,222	.get_brightness = nv50_get_intensity,223	.update_status = nv50_set_intensity,224};225 226/* FIXME: perform backlight probing for eDP _before_ this, this only gets called after connector227 * registration which happens after the initial modeset228 */229static int230nv50_backlight_init(struct nouveau_backlight *bl,231		    struct nouveau_connector *nv_conn,232		    struct nouveau_encoder *nv_encoder,233		    struct backlight_properties *props,234		    const struct backlight_ops **ops)235{236	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);237 238	/*239	 * Note when this runs the connectors have not been probed yet,240	 * so nv_conn->base.status is not set yet.241	 */242	if (nvif_outp_bl_get(&nv_encoder->outp) < 0 ||243	    drm_helper_probe_detect(&nv_conn->base, NULL, false) != connector_status_connected)244		return -ENODEV;245 246	if (nv_conn->type == DCB_CONNECTOR_eDP) {247		int ret;248		u16 current_level;249		u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];250		u8 current_mode;251 252		ret = drm_dp_dpcd_read(&nv_conn->aux, DP_EDP_DPCD_REV, edp_dpcd,253				       EDP_DISPLAY_CTL_CAP_SIZE);254		if (ret < 0)255			return ret;256 257		/* TODO: Add support for hybrid PWM/DPCD panels */258		if (drm_edp_backlight_supported(edp_dpcd) &&259		    (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&260		    (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {261			NV_DEBUG(drm, "DPCD backlight controls supported on %s\n",262				 nv_conn->base.name);263 264			ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info, 0, edp_dpcd,265						     &current_level, &current_mode);266			if (ret < 0)267				return ret;268 269			ret = drm_edp_backlight_enable(&nv_conn->aux, &bl->edp_info, current_level);270			if (ret < 0) {271				NV_ERROR(drm, "Failed to enable backlight on %s: %d\n",272					 nv_conn->base.name, ret);273				return ret;274			}275 276			*ops = &nv50_edp_bl_ops;277			props->brightness = current_level;278			props->max_brightness = bl->edp_info.max;279			bl->uses_dpcd = true;280			return 0;281		}282	}283 284	*ops = &nv50_bl_ops;285	props->max_brightness = 100;286	return 0;287}288 289int290nouveau_backlight_init(struct drm_connector *connector)291{292	struct nouveau_drm *drm = nouveau_drm(connector->dev);293	struct nouveau_backlight *bl;294	struct nouveau_encoder *nv_encoder = NULL;295	struct nvif_device *device = &drm->client.device;296	char backlight_name[BL_NAME_SIZE];297	struct backlight_properties props = {0};298	const struct backlight_ops *ops;299	int ret;300 301	if (apple_gmux_present()) {302		NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");303		return 0;304	}305 306	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)307		nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);308	else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)309		nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);310	else311		return 0;312 313	if (!nv_encoder)314		return 0;315 316	bl = kzalloc(sizeof(*bl), GFP_KERNEL);317	if (!bl)318		return -ENOMEM;319 320	switch (device->info.family) {321	case NV_DEVICE_INFO_V0_CURIE:322		ret = nv40_backlight_init(nv_encoder, &props, &ops);323		break;324	case NV_DEVICE_INFO_V0_TESLA:325	case NV_DEVICE_INFO_V0_FERMI:326	case NV_DEVICE_INFO_V0_KEPLER:327	case NV_DEVICE_INFO_V0_MAXWELL:328	case NV_DEVICE_INFO_V0_PASCAL:329	case NV_DEVICE_INFO_V0_VOLTA:330	case NV_DEVICE_INFO_V0_TURING:331	case NV_DEVICE_INFO_V0_AMPERE: //XXX: not confirmed332		ret = nv50_backlight_init(bl, nouveau_connector(connector),333					  nv_encoder, &props, &ops);334		break;335	default:336		ret = 0;337		goto fail_alloc;338	}339 340	if (ret) {341		if (ret == -ENODEV)342			ret = 0;343		goto fail_alloc;344	}345 346	if (!nouveau_acpi_video_backlight_use_native()) {347		NV_INFO(drm, "Skipping nv_backlight registration\n");348		goto fail_alloc;349	}350 351	if (!nouveau_get_backlight_name(backlight_name, bl)) {352		NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");353		goto fail_alloc;354	}355 356	props.type = BACKLIGHT_RAW;357	bl->dev = backlight_device_register(backlight_name, connector->kdev,358					    nv_encoder, ops, &props);359	if (IS_ERR(bl->dev)) {360		if (bl->id >= 0)361			ida_free(&bl_ida, bl->id);362		ret = PTR_ERR(bl->dev);363		goto fail_alloc;364	}365 366	nouveau_connector(connector)->backlight = bl;367	if (!bl->dev->props.brightness)368		bl->dev->props.brightness =369			bl->dev->ops->get_brightness(bl->dev);370	backlight_update_status(bl->dev);371 372	return 0;373 374fail_alloc:375	kfree(bl);376	/*377	 * If we get here we have an internal panel, but no nv_backlight,378	 * try registering an ACPI video backlight device instead.379	 */380	if (ret == 0)381		nouveau_acpi_video_register_backlight();382 383	return ret;384}385 386void387nouveau_backlight_fini(struct drm_connector *connector)388{389	struct nouveau_connector *nv_conn = nouveau_connector(connector);390	struct nouveau_backlight *bl = nv_conn->backlight;391 392	if (!bl)393		return;394 395	if (bl->id >= 0)396		ida_free(&bl_ida, bl->id);397 398	backlight_device_unregister(bl->dev);399	nv_conn->backlight = NULL;400	kfree(bl);401}402 403void404nouveau_backlight_ctor(void)405{406	ida_init(&bl_ida);407}408 409void410nouveau_backlight_dtor(void)411{412	ida_destroy(&bl_ida);413}414