freebsd-nq/sys/arm/nvidia/drm2/tegra_drm_subr.c
Michal Meloun a0a23564a3 Implement drivers for NVIDIA tegra124 display controller, HDMI source
and host1x module. Unfortunately, tegra124 SoC doesn't have 2D acceleration
engine and 3D requires not yet started nouveau driver.

These drivers forms a first non-x86 DRM2 enabled graphic stack.

Note, there are 2 outstanding issues:
 - The code uses gross hack in order to be comply with
   OBJT_MGTDEVICE pager. (See tegra_bo_init_pager() in tegra_bo.c)
 - Due to improper(probably) refcounting in drm_gem_mmap_single()
   (in drm_gem.c), the gem objects are never released.
I hope that I will be able to address both issues in finite time,
but I don't want to touch x86 world now.

MFC after: 1 month
2016-12-26 14:36:05 +00:00

177 lines
4.6 KiB
C

/*-
* Copyright (c) 2015 Michal Meloun
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <dev/extres/clk/clk.h>
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm_crtc.h>
#include <dev/drm2/drm_crtc_helper.h>
#include <dev/drm2/drm_edid.h>
#include <dev/drm2/drm_fb_helper.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/nvidia/drm2/tegra_drm.h>
#include <gnu/dts/include/dt-bindings/gpio/gpio.h>
int
tegra_drm_connector_get_modes(struct drm_connector *connector)
{
struct tegra_drm_encoder *output;
struct edid *edid = NULL;
int rv;
output = container_of(connector, struct tegra_drm_encoder,
connector);
/* Panel is first */
if (output->panel != NULL) {
/* XXX panel parsing */
return (0);
}
/* static EDID is second*/
edid = output->edid;
/* EDID from monitor is last */
if (edid == NULL)
edid = drm_get_edid(connector, output->ddc);
if (edid == NULL)
return (0);
/* Process EDID */
drm_mode_connector_update_edid_property(connector, edid);
rv = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
return (rv);
}
struct drm_encoder *
tegra_drm_connector_best_encoder(struct drm_connector *connector)
{
struct tegra_drm_encoder *output;
output = container_of(connector, struct tegra_drm_encoder,
connector);
return &(output->encoder);
}
enum drm_connector_status
tegra_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct tegra_drm_encoder *output;
bool active;
int rv;
output = container_of(connector, struct tegra_drm_encoder,
connector);
if (output->gpio_hpd == NULL) {
return ((output->panel != NULL) ?
connector_status_connected:
connector_status_disconnected);
}
rv = gpio_pin_is_active(output->gpio_hpd, &active);
if (rv != 0) {
device_printf(output->dev, " GPIO read failed: %d\n", rv);
return (connector_status_unknown);
}
return (active ?
connector_status_connected : connector_status_disconnected);
}
int
tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node)
{
int rv;
phandle_t ddc;
/* XXX parse output panel here */
rv = OF_getencprop_alloc(node, "nvidia,edid", 1,
(void **)&output->edid);
/* EDID exist but have invalid size */
if ((rv >= 0) && (rv != sizeof(struct edid))) {
device_printf(output->dev,
"Malformed \"nvidia,edid\" property\n");
if (output->edid != NULL)
free(output->edid, M_OFWPROP);
return (ENXIO);
}
gpio_pin_get_by_ofw_property(output->dev, node, "nvidia,hpd-gpio",
&output->gpio_hpd);
ddc = 0;
OF_getencprop(node, "nvidia,ddc-i2c-bus", &ddc, sizeof(ddc));
if (ddc > 0)
output->ddc = OF_device_from_xref(ddc);
if ((output->edid == NULL) && (output->ddc == NULL))
return (ENXIO);
if (output->gpio_hpd != NULL) {
output->connector.polled =
// DRM_CONNECTOR_POLL_HPD;
DRM_CONNECTOR_POLL_DISCONNECT |
DRM_CONNECTOR_POLL_CONNECT;
}
return (0);
}
int tegra_drm_encoder_init(struct tegra_drm_encoder *output,
struct tegra_drm *drm)
{
if (output->panel) {
/* attach panel */
}
return (0);
}
int tegra_drm_encoder_exit(struct tegra_drm_encoder *output,
struct tegra_drm *drm)
{
if (output->panel) {
/* detach panel */
}
return (0);
}