Staging fixes for 3.8-rc3
Here are a number of small fixes to staging drivers for your 3.8-rc3 tree. Well, the omapdrm fixes aren't really "small" but they were waiting on a number of other drm patches to go in through the drm tree, and got delayed by my vacation over the holidays. They are totally self-contained, everyone involved have acked them, and they fix issues that people have been having with the driver. Other than that one, it's a bunch of tiny bugfixes for a number of reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlDzjqQACgkQMUfUDdst+ym/FgCfTOKtRk2YP9FJ+GlJnO7Ij2Ez r/AAoJhhlxUwn59zhyCA6iIOLLix0tfU =llcV -----END PGP SIGNATURE----- Merge tag 'staging-3.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging fixes from Greg Kroah-Hartman: "Here are a number of small fixes to staging drivers for your 3.8-rc3 tree. Well, the omapdrm fixes aren't really "small" but they were waiting on a number of other drm patches to go in through the drm tree, and got delayed by my vacation over the holidays. They are totally self-contained, everyone involved have acked them, and they fix issues that people have been having with the driver. Other than that one, it's a bunch of tiny bugfixes for a number of reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" * tag 'staging-3.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (36 commits) staging: zram: fix invalid memory references during disk write staging: tidspbridge: use prepare/unprepare on dsp clocks staging: tidspbridge: Fix build breakage due to splitting CM functions. staging: comedi: comedi_test: fix race when cancelling command staging: comedi: Kconfig: COMEDI_NI_AT_A2150 should select COMEDI_FC staging: comedi: prevent auto-unconfig of manually configured devices staging: comedi: fix minimum AO period for NI 625x and NI 628x staging: vme_pio2: fix oops on module unloading staging: speakup: avoid out-of-range access in synth_add() staging: speakup: avoid out-of-range access in synth_init() staging: rtl8192e: Fix failure to check pci_map_single() staging: rtl8187se: Fix failure to check pci_map_single() staging: drm/imx: fix double free bug in error path staging: drm/imx: several bug fixes staging: drm/imx: check return value of ipu_reset() staging: drm/omap: fix flags in dma buf exporting staging: drm/omap: use omapdss low level API staging/fwserial: Update TODO file per reviewer comments staging/fwserial: Limit tx/rx to 1394-2008 spec maximum staging/fwserial: Refine Kconfig help text ...
This commit is contained in:
commit
b87fc3e6e2
|
@ -8,6 +8,7 @@ config HID_SENSOR_ACCEL_3D
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Accelerometers 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
|
|
|
@ -411,7 +411,11 @@ static int ad7266_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
st->vref_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref_uv = ret;
|
||||
} else {
|
||||
/* Use internal reference */
|
||||
st->vref_uv = 2500000;
|
||||
|
|
|
@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
|||
*timestamp = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->buffer);
|
||||
iio_push_to_buffers(idev, (u8 *)st->buffer);
|
||||
|
||||
iio_trigger_notify_done(idev->trig);
|
||||
|
||||
|
|
|
@ -1605,19 +1605,20 @@ static int max1363_probe(struct i2c_client *client,
|
|||
|
||||
return 0;
|
||||
error_free_irq:
|
||||
free_irq(st->client->irq, indio_dev);
|
||||
if (client->irq)
|
||||
free_irq(st->client->irq, indio_dev);
|
||||
error_uninit_buffer:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
max1363_buffer_cleanup(indio_dev);
|
||||
error_free_available_scan_masks:
|
||||
kfree(indio_dev->available_scan_masks);
|
||||
error_unregister_map:
|
||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
regulator_put(st->reg);
|
||||
error_unregister_map:
|
||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||
error_free_device:
|
||||
iio_device_free(indio_dev);
|
||||
error_out:
|
||||
|
@ -1635,10 +1636,8 @@ static int max1363_remove(struct i2c_client *client)
|
|||
iio_buffer_unregister(indio_dev);
|
||||
max1363_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->available_scan_masks);
|
||||
if (!IS_ERR(st->reg)) {
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
}
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common"
|
|||
config HID_SENSOR_IIO_COMMON
|
||||
tristate "Common modules for all HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for HID sensor to use
|
||||
HID sensor common processing for attributes and IIO triggers.
|
||||
|
@ -14,6 +14,17 @@ config HID_SENSOR_IIO_COMMON
|
|||
HID sensor drivers, this module contains processing for those
|
||||
attributes.
|
||||
|
||||
config HID_SENSOR_IIO_TRIGGER
|
||||
tristate "Common module (trigger) for all HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON
|
||||
select IIO_TRIGGER
|
||||
help
|
||||
Say yes here to build trigger support for HID sensors.
|
||||
Triggers will be send if all requested attributes were read.
|
||||
|
||||
If this driver is compiled as a module, it will be named
|
||||
hid-sensor-trigger.
|
||||
|
||||
config HID_SENSOR_ENUM_BASE_QUIRKS
|
||||
bool "ENUM base quirks for HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_IIO_COMMON
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
|
||||
hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o
|
||||
obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o
|
||||
hid-sensor-iio-common-y := hid-sensor-attributes.o
|
||||
|
|
|
@ -406,7 +406,11 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,
|
|||
goto error_free_reg;
|
||||
}
|
||||
|
||||
st->vref = regulator_get_voltage(st->vref_reg);
|
||||
ret = regulator_get_voltage(st->vref_reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref = ret;
|
||||
} else {
|
||||
st->vref = st->chip_info->int_vref;
|
||||
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
||||
|
|
|
@ -226,7 +226,11 @@ static int ad5446_probe(struct device *dev, const char *name,
|
|||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
voltage_uv = regulator_get_voltage(reg);
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
|
|
|
@ -296,7 +296,11 @@ static int ad5504_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
voltage_uv = regulator_get_voltage(reg);
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
|
|
@ -238,7 +238,11 @@ static int ad5624r_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
|
|
@ -332,7 +332,11 @@ static int ad5686_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
|
|
|
@ -365,7 +365,11 @@ static int ad5791_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg_pos;
|
||||
|
||||
pos_voltage_uv = regulator_get_voltage(st->reg_vdd);
|
||||
ret = regulator_get_voltage(st->reg_vdd);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_pos;
|
||||
|
||||
pos_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->reg_vss = regulator_get(&spi->dev, "vss");
|
||||
|
@ -374,7 +378,11 @@ static int ad5791_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_put_reg_neg;
|
||||
|
||||
neg_voltage_uv = regulator_get_voltage(st->reg_vss);
|
||||
ret = regulator_get_voltage(st->reg_vss);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
neg_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->pwr_down = true;
|
||||
|
@ -428,6 +436,7 @@ error_put_reg_neg:
|
|||
if (!IS_ERR(st->reg_vss))
|
||||
regulator_put(st->reg_vss);
|
||||
|
||||
error_disable_reg_pos:
|
||||
if (!IS_ERR(st->reg_vdd))
|
||||
regulator_disable(st->reg_vdd);
|
||||
error_put_reg_pos:
|
||||
|
|
|
@ -173,7 +173,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
|
|||
} while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
|
||||
} while (r_cnt == 0);
|
||||
|
||||
tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
|
||||
tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1);
|
||||
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
|
||||
st->r0_fract = do_div(tmp, st->r1_mod);
|
||||
st->r0_int = tmp;
|
||||
|
|
|
@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Gyroscope 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
|
|
|
@ -47,6 +47,7 @@ config HID_SENSOR_ALS
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID ALS"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
|
|
|
@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Magenetometer 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
|
|
|
@ -444,6 +444,7 @@ config COMEDI_ADQ12B
|
|||
|
||||
config COMEDI_NI_AT_A2150
|
||||
tristate "NI AT-A2150 ISA card support"
|
||||
select COMEDI_FC
|
||||
depends on VIRT_TO_BUS
|
||||
---help---
|
||||
Enable support for National Instruments AT-A2150 cards
|
||||
|
|
|
@ -1549,6 +1549,9 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|||
if (cmd == COMEDI_DEVCONFIG) {
|
||||
rc = do_devconfig_ioctl(dev,
|
||||
(struct comedi_devconfig __user *)arg);
|
||||
if (rc == 0)
|
||||
/* Evade comedi_auto_unconfig(). */
|
||||
dev_file_info->hardware_device = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ static int waveform_ai_cancel(struct comedi_device *dev,
|
|||
struct waveform_private *devpriv = dev->private;
|
||||
|
||||
devpriv->timer_running = 0;
|
||||
del_timer(&devpriv->timer);
|
||||
del_timer_sync(&devpriv->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -963,7 +963,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -982,7 +982,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1001,7 +1001,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1037,7 +1037,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 32,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1056,7 +1056,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 32,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1092,7 +1092,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_628x_ao,
|
||||
.reg_type = ni_reg_628x,
|
||||
.ao_unipolar = 1,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1111,7 +1111,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_628x_ao,
|
||||
.reg_type = ni_reg_628x,
|
||||
.ao_unipolar = 1,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
@ -1147,7 +1147,7 @@ static const struct ni_board_struct ni_boards[] = {
|
|||
.ao_range_table = &range_ni_M_628x_ao,
|
||||
.reg_type = ni_reg_628x,
|
||||
.ao_unipolar = 1,
|
||||
.ao_speed = 357,
|
||||
.ao_speed = 350,
|
||||
.num_p0_dio_channels = 32,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
|
|
|
@ -3,7 +3,9 @@ config FIREWIRE_SERIAL
|
|||
depends on FIREWIRE
|
||||
help
|
||||
This enables TTY over IEEE 1394, providing high-speed serial
|
||||
connectivity to cabled peers.
|
||||
connectivity to cabled peers. This driver implements a
|
||||
ad-hoc transport protocol and is currently limited to
|
||||
Linux-to-Linux communication.
|
||||
|
||||
To compile this driver as a module, say M here: the module will
|
||||
be called firewire-serial.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
TODOs
|
||||
-----
|
||||
TODOs prior to this driver moving out of staging
|
||||
------------------------------------------------
|
||||
1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR
|
||||
- I/O is handled asynchronously which presents some issues when error
|
||||
conditions occur.
|
||||
|
@ -11,17 +11,9 @@ TODOs
|
|||
-- Issues with firewire stack --
|
||||
1. This driver uses the same unregistered vendor id that the firewire core does
|
||||
(0xd00d1e). Perhaps this could be exposed as a define in
|
||||
firewire-constants.h?
|
||||
2. MAX_ASYNC_PAYLOAD needs to be publicly exposed by core/ohci
|
||||
- otherwise how will this driver know the max size of address window to
|
||||
open for one packet write?
|
||||
firewire.h?
|
||||
3. Maybe device_max_receive() and link_speed_to_max_payload() should be
|
||||
taken up by the firewire core?
|
||||
4. To avoid dropping rx data while still limiting the maximum buffering,
|
||||
the size of the AR context must be known. How to expose this to drivers?
|
||||
5. Explore if bigger AR context will reduce RCODE_BUSY responses
|
||||
(or auto-grow to certain max size -- but this would require major surgery
|
||||
as the current AR is contiguously mapped)
|
||||
|
||||
-- Issues with TTY core --
|
||||
1. Hack for alternate device name scheme
|
||||
|
|
|
@ -179,7 +179,7 @@ static void dump_profile(struct seq_file *m, struct stats *stats)
|
|||
/* Returns the max receive packet size for the given card */
|
||||
static inline int device_max_receive(struct fw_device *fw_device)
|
||||
{
|
||||
return 1 << (clamp_t(int, fw_device->max_rec, 8U, 13U) + 1);
|
||||
return 1 << (clamp_t(int, fw_device->max_rec, 8U, 11U) + 1);
|
||||
}
|
||||
|
||||
static void fwtty_log_tx_error(struct fwtty_port *port, int rcode)
|
||||
|
|
|
@ -374,10 +374,10 @@ static inline void fwtty_bind_console(struct fwtty_port *port,
|
|||
*/
|
||||
static inline int link_speed_to_max_payload(unsigned speed)
|
||||
{
|
||||
static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, };
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200);
|
||||
static const int max_async[] = { 307, 614, 1229, 2458, };
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_800);
|
||||
|
||||
speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_3200);
|
||||
speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_800);
|
||||
if (limit_bw)
|
||||
return max_async[speed];
|
||||
else
|
||||
|
|
|
@ -27,8 +27,8 @@ config ADIS16130
|
|||
config ADIS16260
|
||||
tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16260 ADIS16265
|
||||
ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors.
|
||||
|
|
|
@ -584,7 +584,6 @@ int imx_drm_add_encoder(struct drm_encoder *encoder,
|
|||
|
||||
ret = imx_drm_encoder_register(imx_drm_encoder);
|
||||
if (ret) {
|
||||
kfree(imx_drm_encoder);
|
||||
ret = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
|
|
|
@ -1104,7 +1104,9 @@ static int ipu_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out_failed_irq;
|
||||
|
||||
ipu_reset(ipu);
|
||||
ret = ipu_reset(ipu);
|
||||
if (ret)
|
||||
goto out_failed_reset;
|
||||
|
||||
/* Set MCU_T to divide MCU access window into 2 */
|
||||
ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
|
||||
|
@ -1129,6 +1131,7 @@ failed_add_clients:
|
|||
ipu_submodules_exit(ipu);
|
||||
failed_submodules_init:
|
||||
ipu_irq_exit(ipu);
|
||||
out_failed_reset:
|
||||
out_failed_irq:
|
||||
clk_disable_unprepare(ipu->clk);
|
||||
failed_clk_get:
|
||||
|
|
|
@ -452,7 +452,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
|
|||
int ret;
|
||||
|
||||
ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
|
||||
if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) {
|
||||
if (IS_ERR(ipu_crtc->ipu_ch)) {
|
||||
ret = PTR_ERR(ipu_crtc->ipu_ch);
|
||||
goto err_out;
|
||||
}
|
||||
|
@ -472,7 +472,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
|
|||
if (pdata->dp >= 0) {
|
||||
ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
|
||||
if (IS_ERR(ipu_crtc->dp)) {
|
||||
ret = PTR_ERR(ipu_crtc->ipu_ch);
|
||||
ret = PTR_ERR(ipu_crtc->dp);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
@ -548,6 +548,8 @@ static int ipu_drm_probe(struct platform_device *pdev)
|
|||
ipu_crtc->dev = &pdev->dev;
|
||||
|
||||
ret = ipu_crtc_init(ipu_crtc, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, ipu_crtc);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
ccflags-y := -Iinclude/drm -Werror
|
||||
omapdrm-y := omap_drv.o \
|
||||
omap_irq.o \
|
||||
omap_debugfs.o \
|
||||
omap_crtc.o \
|
||||
omap_plane.o \
|
||||
|
|
|
@ -17,9 +17,6 @@ TODO
|
|||
. Revisit GEM sync object infrastructure.. TTM has some framework for this
|
||||
already. Possibly this could be refactored out and made more common?
|
||||
There should be some way to do this with less wheel-reinvention.
|
||||
. Review DSS vs KMS mismatches. The omap_dss_device is sort of part encoder,
|
||||
part connector. Which results in a bit of duct tape to fwd calls from
|
||||
encoder to connector. Possibly this could be done a bit better.
|
||||
. Solve PM sequencing on resume. DMM/TILER must be reloaded before any
|
||||
access is made from any component in the system. Which means on suspend
|
||||
CRTC's should be disabled, and on resume the LUT should be reprogrammed
|
||||
|
|
|
@ -31,9 +31,10 @@
|
|||
struct omap_connector {
|
||||
struct drm_connector base;
|
||||
struct omap_dss_device *dssdev;
|
||||
struct drm_encoder *encoder;
|
||||
};
|
||||
|
||||
static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
|
||||
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
mode->clock = timings->pixel_clock;
|
||||
|
@ -64,7 +65,7 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
|
|||
mode->flags |= DRM_MODE_FLAG_NVSYNC;
|
||||
}
|
||||
|
||||
static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
|
||||
void copy_timings_drm_to_omap(struct omap_video_timings *timings,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
timings->pixel_clock = mode->clock;
|
||||
|
@ -96,48 +97,7 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
|
|||
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
|
||||
}
|
||||
|
||||
static void omap_connector_dpms(struct drm_connector *connector, int mode)
|
||||
{
|
||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
||||
int old_dpms;
|
||||
|
||||
DBG("%s: %d", dssdev->name, mode);
|
||||
|
||||
old_dpms = connector->dpms;
|
||||
|
||||
/* from off to on, do from crtc to connector */
|
||||
if (mode < old_dpms)
|
||||
drm_helper_connector_dpms(connector, mode);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
/* store resume info for suspended displays */
|
||||
switch (dssdev->state) {
|
||||
case OMAP_DSS_DISPLAY_SUSPENDED:
|
||||
dssdev->activate_after_resume = true;
|
||||
break;
|
||||
case OMAP_DSS_DISPLAY_DISABLED: {
|
||||
int ret = dssdev->driver->enable(dssdev);
|
||||
if (ret) {
|
||||
DBG("%s: failed to enable: %d",
|
||||
dssdev->name, ret);
|
||||
dssdev->driver->disable(dssdev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
/* from on to off, do from connector to crtc */
|
||||
if (mode > old_dpms)
|
||||
drm_helper_connector_dpms(connector, mode);
|
||||
}
|
||||
|
||||
enum drm_connector_status omap_connector_detect(
|
||||
static enum drm_connector_status omap_connector_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||
|
@ -164,8 +124,6 @@ static void omap_connector_destroy(struct drm_connector *connector)
|
|||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
||||
|
||||
dssdev->driver->disable(dssdev);
|
||||
|
||||
DBG("%s", omap_connector->dssdev->name);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
|
@ -261,36 +219,12 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
|
|||
struct drm_encoder *omap_connector_attached_encoder(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
int i;
|
||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
struct drm_mode_object *obj;
|
||||
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
obj = drm_mode_object_find(connector->dev,
|
||||
connector->encoder_ids[i],
|
||||
DRM_MODE_OBJECT_ENCODER);
|
||||
|
||||
if (obj) {
|
||||
struct drm_encoder *encoder = obj_to_encoder(obj);
|
||||
struct omap_overlay_manager *mgr =
|
||||
omap_encoder_get_manager(encoder);
|
||||
DBG("%s: found %s", omap_connector->dssdev->name,
|
||||
mgr->name);
|
||||
return encoder;
|
||||
}
|
||||
}
|
||||
|
||||
DBG("%s: no encoder", omap_connector->dssdev->name);
|
||||
|
||||
return NULL;
|
||||
return omap_connector->encoder;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs omap_connector_funcs = {
|
||||
.dpms = omap_connector_dpms,
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = omap_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = omap_connector_destroy,
|
||||
|
@ -302,34 +236,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
|
|||
.best_encoder = omap_connector_attached_encoder,
|
||||
};
|
||||
|
||||
/* called from encoder when mode is set, to propagate settings to the dssdev */
|
||||
void omap_connector_mode_set(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
||||
struct omap_dss_driver *dssdrv = dssdev->driver;
|
||||
struct omap_video_timings timings = {0};
|
||||
|
||||
copy_timings_drm_to_omap(&timings, mode);
|
||||
|
||||
DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
|
||||
omap_connector->dssdev->name,
|
||||
mode->base.id, mode->name, mode->vrefresh, mode->clock,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal, mode->type, mode->flags);
|
||||
|
||||
if (dssdrv->check_timings(dssdev, &timings)) {
|
||||
dev_err(dev->dev, "could not set timings\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dssdrv->set_timings(dssdev, &timings);
|
||||
}
|
||||
|
||||
/* flush an area of the framebuffer (in case of manual update display that
|
||||
* is not automatically flushed)
|
||||
*/
|
||||
|
@ -344,7 +250,8 @@ void omap_connector_flush(struct drm_connector *connector,
|
|||
|
||||
/* initialize connector */
|
||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
int connector_type, struct omap_dss_device *dssdev)
|
||||
int connector_type, struct omap_dss_device *dssdev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct omap_connector *omap_connector;
|
||||
|
@ -360,6 +267,8 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
|
|||
}
|
||||
|
||||
omap_connector->dssdev = dssdev;
|
||||
omap_connector->encoder = encoder;
|
||||
|
||||
connector = &omap_connector->base;
|
||||
|
||||
drm_connector_init(dev, connector, &omap_connector_funcs,
|
||||
|
|
|
@ -28,19 +28,131 @@
|
|||
struct omap_crtc {
|
||||
struct drm_crtc base;
|
||||
struct drm_plane *plane;
|
||||
|
||||
const char *name;
|
||||
int id;
|
||||
int pipe;
|
||||
enum omap_channel channel;
|
||||
struct omap_overlay_manager_info info;
|
||||
|
||||
/*
|
||||
* Temporary: eventually this will go away, but it is needed
|
||||
* for now to keep the output's happy. (They only need
|
||||
* mgr->id.) Eventually this will be replaced w/ something
|
||||
* more common-panel-framework-y
|
||||
*/
|
||||
struct omap_overlay_manager mgr;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
bool enabled;
|
||||
bool full_update;
|
||||
|
||||
struct omap_drm_apply apply;
|
||||
|
||||
struct omap_drm_irq apply_irq;
|
||||
struct omap_drm_irq error_irq;
|
||||
|
||||
/* list of in-progress apply's: */
|
||||
struct list_head pending_applies;
|
||||
|
||||
/* list of queued apply's: */
|
||||
struct list_head queued_applies;
|
||||
|
||||
/* for handling queued and in-progress applies: */
|
||||
struct work_struct apply_work;
|
||||
|
||||
/* if there is a pending flip, these will be non-null: */
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_framebuffer *old_fb;
|
||||
|
||||
/* for handling page flips without caring about what
|
||||
* the callback is called from. Possibly we should just
|
||||
* make omap_gem always call the cb from the worker so
|
||||
* we don't have to care about this..
|
||||
*
|
||||
* XXX maybe fold into apply_work??
|
||||
*/
|
||||
struct work_struct page_flip_work;
|
||||
};
|
||||
|
||||
/*
|
||||
* Manager-ops, callbacks from output when they need to configure
|
||||
* the upstream part of the video pipe.
|
||||
*
|
||||
* Most of these we can ignore until we add support for command-mode
|
||||
* panels.. for video-mode the crtc-helpers already do an adequate
|
||||
* job of sequencing the setup of the video pipe in the proper order
|
||||
*/
|
||||
|
||||
/* we can probably ignore these until we support command-mode panels: */
|
||||
static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
}
|
||||
|
||||
static int omap_crtc_enable(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_crtc_disable(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
}
|
||||
|
||||
static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
|
||||
const struct omap_video_timings *timings)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
|
||||
DBG("%s", omap_crtc->name);
|
||||
omap_crtc->timings = *timings;
|
||||
omap_crtc->full_update = true;
|
||||
}
|
||||
|
||||
static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
|
||||
const struct dss_lcd_mgr_config *config)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
|
||||
DBG("%s", omap_crtc->name);
|
||||
dispc_mgr_set_lcd_config(omap_crtc->channel, config);
|
||||
}
|
||||
|
||||
static int omap_crtc_register_framedone_handler(
|
||||
struct omap_overlay_manager *mgr,
|
||||
void (*handler)(void *), void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_crtc_unregister_framedone_handler(
|
||||
struct omap_overlay_manager *mgr,
|
||||
void (*handler)(void *), void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct dss_mgr_ops mgr_ops = {
|
||||
.start_update = omap_crtc_start_update,
|
||||
.enable = omap_crtc_enable,
|
||||
.disable = omap_crtc_disable,
|
||||
.set_timings = omap_crtc_set_timings,
|
||||
.set_lcd_config = omap_crtc_set_lcd_config,
|
||||
.register_framedone_handler = omap_crtc_register_framedone_handler,
|
||||
.unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
|
||||
};
|
||||
|
||||
/*
|
||||
* CRTC funcs:
|
||||
*/
|
||||
|
||||
static void omap_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
|
||||
DBG("%s", omap_crtc->name);
|
||||
|
||||
WARN_ON(omap_crtc->apply_irq.registered);
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
|
||||
|
||||
omap_crtc->plane->funcs->destroy(omap_crtc->plane);
|
||||
drm_crtc_cleanup(crtc);
|
||||
|
||||
kfree(omap_crtc);
|
||||
}
|
||||
|
||||
|
@ -48,14 +160,25 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||
{
|
||||
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||
int i;
|
||||
|
||||
WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
|
||||
DBG("%s: %d", omap_crtc->name, mode);
|
||||
|
||||
for (i = 0; i < priv->num_planes; i++) {
|
||||
struct drm_plane *plane = priv->planes[i];
|
||||
if (plane->crtc == crtc)
|
||||
WARN_ON(omap_plane_dpms(plane, mode));
|
||||
if (enabled != omap_crtc->enabled) {
|
||||
omap_crtc->enabled = enabled;
|
||||
omap_crtc->full_update = true;
|
||||
omap_crtc_apply(crtc, &omap_crtc->apply);
|
||||
|
||||
/* also enable our private plane: */
|
||||
WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
|
||||
|
||||
/* and any attached overlay planes: */
|
||||
for (i = 0; i < priv->num_planes; i++) {
|
||||
struct drm_plane *plane = priv->planes[i];
|
||||
if (plane->crtc == crtc)
|
||||
WARN_ON(omap_plane_dpms(plane, mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,12 +196,26 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
|
|||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_plane *plane = omap_crtc->plane;
|
||||
|
||||
return omap_plane_mode_set(plane, crtc, crtc->fb,
|
||||
mode = adjusted_mode;
|
||||
|
||||
DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
|
||||
omap_crtc->name, mode->base.id, mode->name,
|
||||
mode->vrefresh, mode->clock,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
mode->type, mode->flags);
|
||||
|
||||
copy_timings_drm_to_omap(&omap_crtc->timings, mode);
|
||||
omap_crtc->full_update = true;
|
||||
|
||||
return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
x << 16, y << 16,
|
||||
mode->hdisplay << 16, mode->vdisplay << 16);
|
||||
mode->hdisplay << 16, mode->vdisplay << 16,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void omap_crtc_prepare(struct drm_crtc *crtc)
|
||||
|
@ -102,10 +239,11 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|||
struct drm_plane *plane = omap_crtc->plane;
|
||||
struct drm_display_mode *mode = &crtc->mode;
|
||||
|
||||
return plane->funcs->update_plane(plane, crtc, crtc->fb,
|
||||
return omap_plane_mode_set(plane, crtc, crtc->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
x << 16, y << 16,
|
||||
mode->hdisplay << 16, mode->vdisplay << 16);
|
||||
mode->hdisplay << 16, mode->vdisplay << 16,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void omap_crtc_load_lut(struct drm_crtc *crtc)
|
||||
|
@ -114,61 +252,52 @@ static void omap_crtc_load_lut(struct drm_crtc *crtc)
|
|||
|
||||
static void vblank_cb(void *arg)
|
||||
{
|
||||
static uint32_t sequence;
|
||||
struct drm_crtc *crtc = arg;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_pending_vblank_event *event = omap_crtc->event;
|
||||
unsigned long flags;
|
||||
struct timeval now;
|
||||
|
||||
WARN_ON(!event);
|
||||
|
||||
omap_crtc->event = NULL;
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
/* wakeup userspace */
|
||||
if (event) {
|
||||
do_gettimeofday(&now);
|
||||
if (omap_crtc->event)
|
||||
drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
/* TODO: we can't yet use the vblank time accounting,
|
||||
* because omapdss lower layer is the one that knows
|
||||
* the irq # and registers the handler, which more or
|
||||
* less defeats how drm_irq works.. for now just fake
|
||||
* the sequence number and use gettimeofday..
|
||||
*
|
||||
event->event.sequence = drm_vblank_count_and_time(
|
||||
dev, omap_crtc->id, &now);
|
||||
*/
|
||||
event->event.sequence = sequence++;
|
||||
event->event.tv_sec = now.tv_sec;
|
||||
event->event.tv_usec = now.tv_usec;
|
||||
list_add_tail(&event->base.link,
|
||||
&event->base.file_priv->event_list);
|
||||
wake_up_interruptible(&event->base.file_priv->event_wait);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
omap_crtc->event = NULL;
|
||||
omap_crtc->old_fb = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static void page_flip_worker(struct work_struct *work)
|
||||
{
|
||||
struct omap_crtc *omap_crtc =
|
||||
container_of(work, struct omap_crtc, page_flip_work);
|
||||
struct drm_crtc *crtc = &omap_crtc->base;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_display_mode *mode = &crtc->mode;
|
||||
struct drm_gem_object *bo;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
crtc->x << 16, crtc->y << 16,
|
||||
mode->hdisplay << 16, mode->vdisplay << 16,
|
||||
vblank_cb, crtc);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
bo = omap_framebuffer_bo(crtc->fb, 0);
|
||||
drm_gem_object_unreference_unlocked(bo);
|
||||
}
|
||||
|
||||
static void page_flip_cb(void *arg)
|
||||
{
|
||||
struct drm_crtc *crtc = arg;
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_framebuffer *old_fb = omap_crtc->old_fb;
|
||||
struct drm_gem_object *bo;
|
||||
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||||
|
||||
omap_crtc->old_fb = NULL;
|
||||
|
||||
omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
|
||||
|
||||
/* really we'd like to setup the callback atomically w/ setting the
|
||||
* new scanout buffer to avoid getting stuck waiting an extra vblank
|
||||
* cycle.. for now go for correctness and later figure out speed..
|
||||
*/
|
||||
omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc);
|
||||
|
||||
bo = omap_framebuffer_bo(crtc->fb, 0);
|
||||
drm_gem_object_unreference_unlocked(bo);
|
||||
/* avoid assumptions about what ctxt we are called from: */
|
||||
queue_work(priv->wq, &omap_crtc->page_flip_work);
|
||||
}
|
||||
|
||||
static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
|
||||
|
@ -179,14 +308,14 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
|
|||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_gem_object *bo;
|
||||
|
||||
DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
|
||||
DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
|
||||
fb->base.id, event);
|
||||
|
||||
if (omap_crtc->event) {
|
||||
if (omap_crtc->old_fb) {
|
||||
dev_err(dev->dev, "already a pending flip\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
omap_crtc->old_fb = crtc->fb;
|
||||
omap_crtc->event = event;
|
||||
crtc->fb = fb;
|
||||
|
||||
|
@ -234,14 +363,244 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
|
|||
.load_lut = omap_crtc_load_lut,
|
||||
};
|
||||
|
||||
const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
return &omap_crtc->timings;
|
||||
}
|
||||
|
||||
enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
return omap_crtc->channel;
|
||||
}
|
||||
|
||||
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct omap_crtc *omap_crtc =
|
||||
container_of(irq, struct omap_crtc, error_irq);
|
||||
struct drm_crtc *crtc = &omap_crtc->base;
|
||||
DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
|
||||
/* avoid getting in a flood, unregister the irq until next vblank */
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
|
||||
}
|
||||
|
||||
static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct omap_crtc *omap_crtc =
|
||||
container_of(irq, struct omap_crtc, apply_irq);
|
||||
struct drm_crtc *crtc = &omap_crtc->base;
|
||||
|
||||
if (!omap_crtc->error_irq.registered)
|
||||
omap_irq_register(crtc->dev, &omap_crtc->error_irq);
|
||||
|
||||
if (!dispc_mgr_go_busy(omap_crtc->channel)) {
|
||||
struct omap_drm_private *priv =
|
||||
crtc->dev->dev_private;
|
||||
DBG("%s: apply done", omap_crtc->name);
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
|
||||
queue_work(priv->wq, &omap_crtc->apply_work);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_worker(struct work_struct *work)
|
||||
{
|
||||
struct omap_crtc *omap_crtc =
|
||||
container_of(work, struct omap_crtc, apply_work);
|
||||
struct drm_crtc *crtc = &omap_crtc->base;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct omap_drm_apply *apply, *n;
|
||||
bool need_apply;
|
||||
|
||||
/*
|
||||
* Synchronize everything on mode_config.mutex, to keep
|
||||
* the callbacks and list modification all serialized
|
||||
* with respect to modesetting ioctls from userspace.
|
||||
*/
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
dispc_runtime_get();
|
||||
|
||||
/*
|
||||
* If we are still pending a previous update, wait.. when the
|
||||
* pending update completes, we get kicked again.
|
||||
*/
|
||||
if (omap_crtc->apply_irq.registered)
|
||||
goto out;
|
||||
|
||||
/* finish up previous apply's: */
|
||||
list_for_each_entry_safe(apply, n,
|
||||
&omap_crtc->pending_applies, pending_node) {
|
||||
apply->post_apply(apply);
|
||||
list_del(&apply->pending_node);
|
||||
}
|
||||
|
||||
need_apply = !list_empty(&omap_crtc->queued_applies);
|
||||
|
||||
/* then handle the next round of of queued apply's: */
|
||||
list_for_each_entry_safe(apply, n,
|
||||
&omap_crtc->queued_applies, queued_node) {
|
||||
apply->pre_apply(apply);
|
||||
list_del(&apply->queued_node);
|
||||
apply->queued = false;
|
||||
list_add_tail(&apply->pending_node,
|
||||
&omap_crtc->pending_applies);
|
||||
}
|
||||
|
||||
if (need_apply) {
|
||||
enum omap_channel channel = omap_crtc->channel;
|
||||
|
||||
DBG("%s: GO", omap_crtc->name);
|
||||
|
||||
if (dispc_mgr_is_enabled(channel)) {
|
||||
omap_irq_register(dev, &omap_crtc->apply_irq);
|
||||
dispc_mgr_go(channel);
|
||||
} else {
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
queue_work(priv->wq, &omap_crtc->apply_work);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
dispc_runtime_put();
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
}
|
||||
|
||||
int omap_crtc_apply(struct drm_crtc *crtc,
|
||||
struct omap_drm_apply *apply)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
|
||||
/* no need to queue it again if it is already queued: */
|
||||
if (apply->queued)
|
||||
return 0;
|
||||
|
||||
apply->queued = true;
|
||||
list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
|
||||
|
||||
/*
|
||||
* If there are no currently pending updates, then go ahead and
|
||||
* kick the worker immediately, otherwise it will run again when
|
||||
* the current update finishes.
|
||||
*/
|
||||
if (list_empty(&omap_crtc->pending_applies)) {
|
||||
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||||
queue_work(priv->wq, &omap_crtc->apply_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called only from apply */
|
||||
static void set_enabled(struct drm_crtc *crtc, bool enable)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
enum omap_channel channel = omap_crtc->channel;
|
||||
struct omap_irq_wait *wait = NULL;
|
||||
|
||||
if (dispc_mgr_is_enabled(channel) == enable)
|
||||
return;
|
||||
|
||||
/* ignore sync-lost irqs during enable/disable */
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
|
||||
|
||||
if (dispc_mgr_get_framedone_irq(channel)) {
|
||||
if (!enable) {
|
||||
wait = omap_irq_wait_init(dev,
|
||||
dispc_mgr_get_framedone_irq(channel), 1);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* When we disable digit output, we need to wait until fields
|
||||
* are done. Otherwise the DSS is still working, and turning
|
||||
* off the clocks prevents DSS from going to OFF mode. And when
|
||||
* enabling, we need to wait for the extra sync losts
|
||||
*/
|
||||
wait = omap_irq_wait_init(dev,
|
||||
dispc_mgr_get_vsync_irq(channel), 2);
|
||||
}
|
||||
|
||||
dispc_mgr_enable(channel, enable);
|
||||
|
||||
if (wait) {
|
||||
int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "%s: timeout waiting for %s\n",
|
||||
omap_crtc->name, enable ? "enable" : "disable");
|
||||
}
|
||||
}
|
||||
|
||||
omap_irq_register(crtc->dev, &omap_crtc->error_irq);
|
||||
}
|
||||
|
||||
static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
|
||||
{
|
||||
struct omap_crtc *omap_crtc =
|
||||
container_of(apply, struct omap_crtc, apply);
|
||||
struct drm_crtc *crtc = &omap_crtc->base;
|
||||
struct drm_encoder *encoder = NULL;
|
||||
|
||||
DBG("%s: enabled=%d, full=%d", omap_crtc->name,
|
||||
omap_crtc->enabled, omap_crtc->full_update);
|
||||
|
||||
if (omap_crtc->full_update) {
|
||||
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||||
int i;
|
||||
for (i = 0; i < priv->num_encoders; i++) {
|
||||
if (priv->encoders[i]->crtc == crtc) {
|
||||
encoder = priv->encoders[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!omap_crtc->enabled) {
|
||||
set_enabled(&omap_crtc->base, false);
|
||||
if (encoder)
|
||||
omap_encoder_set_enabled(encoder, false);
|
||||
} else {
|
||||
if (encoder) {
|
||||
omap_encoder_set_enabled(encoder, false);
|
||||
omap_encoder_update(encoder, &omap_crtc->mgr,
|
||||
&omap_crtc->timings);
|
||||
omap_encoder_set_enabled(encoder, true);
|
||||
omap_crtc->full_update = false;
|
||||
}
|
||||
|
||||
dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
|
||||
dispc_mgr_set_timings(omap_crtc->channel,
|
||||
&omap_crtc->timings);
|
||||
set_enabled(&omap_crtc->base, true);
|
||||
}
|
||||
|
||||
omap_crtc->full_update = false;
|
||||
}
|
||||
|
||||
static void omap_crtc_post_apply(struct omap_drm_apply *apply)
|
||||
{
|
||||
/* nothing needed for post-apply */
|
||||
}
|
||||
|
||||
static const char *channel_names[] = {
|
||||
[OMAP_DSS_CHANNEL_LCD] = "lcd",
|
||||
[OMAP_DSS_CHANNEL_DIGIT] = "tv",
|
||||
[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
|
||||
};
|
||||
|
||||
/* initialize crtc */
|
||||
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
||||
struct omap_overlay *ovl, int id)
|
||||
struct drm_plane *plane, enum omap_channel channel, int id)
|
||||
{
|
||||
struct drm_crtc *crtc = NULL;
|
||||
struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
|
||||
struct omap_crtc *omap_crtc;
|
||||
struct omap_overlay_manager_info *info;
|
||||
|
||||
DBG("%s", ovl->name);
|
||||
DBG("%s", channel_names[channel]);
|
||||
|
||||
omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
|
||||
|
||||
if (!omap_crtc) {
|
||||
dev_err(dev->dev, "could not allocate CRTC\n");
|
||||
|
@ -250,10 +609,40 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
|||
|
||||
crtc = &omap_crtc->base;
|
||||
|
||||
omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true);
|
||||
INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker);
|
||||
INIT_WORK(&omap_crtc->apply_work, apply_worker);
|
||||
|
||||
INIT_LIST_HEAD(&omap_crtc->pending_applies);
|
||||
INIT_LIST_HEAD(&omap_crtc->queued_applies);
|
||||
|
||||
omap_crtc->apply.pre_apply = omap_crtc_pre_apply;
|
||||
omap_crtc->apply.post_apply = omap_crtc_post_apply;
|
||||
|
||||
omap_crtc->apply_irq.irqmask = pipe2vbl(id);
|
||||
omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
|
||||
|
||||
omap_crtc->error_irq.irqmask =
|
||||
dispc_mgr_get_sync_lost_irq(channel);
|
||||
omap_crtc->error_irq.irq = omap_crtc_error_irq;
|
||||
omap_irq_register(dev, &omap_crtc->error_irq);
|
||||
|
||||
omap_crtc->channel = channel;
|
||||
omap_crtc->plane = plane;
|
||||
omap_crtc->plane->crtc = crtc;
|
||||
omap_crtc->name = ovl->name;
|
||||
omap_crtc->id = id;
|
||||
omap_crtc->name = channel_names[channel];
|
||||
omap_crtc->pipe = id;
|
||||
|
||||
/* temporary: */
|
||||
omap_crtc->mgr.id = channel;
|
||||
|
||||
dss_install_mgr_ops(&mgr_ops);
|
||||
|
||||
/* TODO: fix hard-coded setup.. add properties! */
|
||||
info = &omap_crtc->info;
|
||||
info->default_color = 0x00000000;
|
||||
info->trans_key = 0x00000000;
|
||||
info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
|
||||
info->trans_enabled = false;
|
||||
|
||||
drm_crtc_init(dev, crtc, &omap_crtc_funcs);
|
||||
drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
|
||||
|
|
|
@ -74,319 +74,98 @@ static int get_connector_type(struct omap_dss_device *dssdev)
|
|||
}
|
||||
}
|
||||
|
||||
#if 0 /* enable when dss2 supports hotplug */
|
||||
static int omap_drm_notifier(struct notifier_block *nb,
|
||||
unsigned long evt, void *arg)
|
||||
{
|
||||
switch (evt) {
|
||||
case OMAP_DSS_SIZE_CHANGE:
|
||||
case OMAP_DSS_HOTPLUG_CONNECT:
|
||||
case OMAP_DSS_HOTPLUG_DISCONNECT: {
|
||||
struct drm_device *dev = drm_device;
|
||||
DBG("hotplug event: evt=%d, dev=%p", evt, dev);
|
||||
if (dev)
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
default: /* don't care about other events for now */
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dump_video_chains(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
DBG("dumping video chains: ");
|
||||
for (i = 0; i < omap_dss_get_num_overlays(); i++) {
|
||||
struct omap_overlay *ovl = omap_dss_get_overlay(i);
|
||||
struct omap_overlay_manager *mgr = ovl->manager;
|
||||
struct omap_dss_device *dssdev = mgr ?
|
||||
mgr->get_device(mgr) : NULL;
|
||||
if (dssdev) {
|
||||
DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
|
||||
dssdev->name);
|
||||
} else if (mgr) {
|
||||
DBG("%d: %s -> %s", i, ovl->name, mgr->name);
|
||||
} else {
|
||||
DBG("%d: %s", i, ovl->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* create encoders for each manager */
|
||||
static int create_encoder(struct drm_device *dev,
|
||||
struct omap_overlay_manager *mgr)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
|
||||
|
||||
if (!encoder) {
|
||||
dev_err(dev->dev, "could not create encoder: %s\n",
|
||||
mgr->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create connectors for each display device */
|
||||
static int create_connector(struct drm_device *dev,
|
||||
struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
static struct notifier_block *notifier;
|
||||
struct drm_connector *connector;
|
||||
int j;
|
||||
|
||||
if (!dssdev->driver) {
|
||||
dev_warn(dev->dev, "%s has no driver.. skipping it\n",
|
||||
dssdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dssdev->driver->get_timings ||
|
||||
dssdev->driver->read_edid)) {
|
||||
dev_warn(dev->dev, "%s driver does not support "
|
||||
"get_timings or read_edid.. skipping it!\n",
|
||||
dssdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
connector = omap_connector_init(dev,
|
||||
get_connector_type(dssdev), dssdev);
|
||||
|
||||
if (!connector) {
|
||||
dev_err(dev->dev, "could not create connector: %s\n",
|
||||
dssdev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
|
||||
|
||||
priv->connectors[priv->num_connectors++] = connector;
|
||||
|
||||
#if 0 /* enable when dss2 supports hotplug */
|
||||
notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
|
||||
notifier->notifier_call = omap_drm_notifier;
|
||||
omap_dss_add_notify(dssdev, notifier);
|
||||
#else
|
||||
notifier = NULL;
|
||||
#endif
|
||||
|
||||
for (j = 0; j < priv->num_encoders; j++) {
|
||||
struct omap_overlay_manager *mgr =
|
||||
omap_encoder_get_manager(priv->encoders[j]);
|
||||
if (mgr->get_device(mgr) == dssdev) {
|
||||
drm_mode_connector_attach_encoder(connector,
|
||||
priv->encoders[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create up to max_overlays CRTCs mapping to overlays.. by default,
|
||||
* connect the overlays to different managers/encoders, giving priority
|
||||
* to encoders connected to connectors with a detected connection
|
||||
*/
|
||||
static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
|
||||
int *j, unsigned int connected_connectors)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_overlay_manager *mgr = NULL;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/* find next best connector, ones with detected connection first
|
||||
*/
|
||||
while (*j < priv->num_connectors && !mgr) {
|
||||
if (connected_connectors & (1 << *j)) {
|
||||
struct drm_encoder *encoder =
|
||||
omap_connector_attached_encoder(
|
||||
priv->connectors[*j]);
|
||||
if (encoder)
|
||||
mgr = omap_encoder_get_manager(encoder);
|
||||
|
||||
}
|
||||
(*j)++;
|
||||
}
|
||||
|
||||
/* if we couldn't find another connected connector, lets start
|
||||
* looking at the unconnected connectors:
|
||||
*
|
||||
* note: it might not be immediately apparent, but thanks to
|
||||
* the !mgr check in both this loop and the one above, the only
|
||||
* way to enter this loop is with *j == priv->num_connectors,
|
||||
* so idx can never go negative.
|
||||
*/
|
||||
while (*j < 2 * priv->num_connectors && !mgr) {
|
||||
int idx = *j - priv->num_connectors;
|
||||
if (!(connected_connectors & (1 << idx))) {
|
||||
struct drm_encoder *encoder =
|
||||
omap_connector_attached_encoder(
|
||||
priv->connectors[idx]);
|
||||
if (encoder)
|
||||
mgr = omap_encoder_get_manager(encoder);
|
||||
|
||||
}
|
||||
(*j)++;
|
||||
}
|
||||
|
||||
crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
|
||||
|
||||
if (!crtc) {
|
||||
dev_err(dev->dev, "could not create CRTC: %s\n",
|
||||
ovl->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
|
||||
|
||||
priv->crtcs[priv->num_crtcs++] = crtc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_plane(struct drm_device *dev, struct omap_overlay *ovl,
|
||||
unsigned int possible_crtcs)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct drm_plane *plane =
|
||||
omap_plane_init(dev, ovl, possible_crtcs, false);
|
||||
|
||||
if (!plane) {
|
||||
dev_err(dev->dev, "could not create plane: %s\n",
|
||||
ovl->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
|
||||
|
||||
priv->planes[priv->num_planes++] = plane;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_dev_name(struct omap_dss_device *dssdev, void *data)
|
||||
{
|
||||
return !strcmp(dssdev->name, data);
|
||||
}
|
||||
|
||||
static unsigned int detect_connectors(struct drm_device *dev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
unsigned int connected_connectors = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_connectors; i++) {
|
||||
struct drm_connector *connector = priv->connectors[i];
|
||||
if (omap_connector_detect(connector, true) ==
|
||||
connector_status_connected) {
|
||||
connected_connectors |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
return connected_connectors;
|
||||
}
|
||||
|
||||
static int omap_modeset_init(struct drm_device *dev)
|
||||
{
|
||||
const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
|
||||
struct omap_kms_platform_data *kms_pdata = NULL;
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
int i, j;
|
||||
unsigned int connected_connectors = 0;
|
||||
int num_ovls = dss_feat_get_num_ovls();
|
||||
int id;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
|
||||
if (pdata && pdata->kms_pdata) {
|
||||
kms_pdata = pdata->kms_pdata;
|
||||
omap_drm_irq_install(dev);
|
||||
|
||||
/* if platform data is provided by the board file, use it to
|
||||
* control which overlays, managers, and devices we own.
|
||||
*/
|
||||
for (i = 0; i < kms_pdata->mgr_cnt; i++) {
|
||||
struct omap_overlay_manager *mgr =
|
||||
omap_dss_get_overlay_manager(
|
||||
kms_pdata->mgr_ids[i]);
|
||||
create_encoder(dev, mgr);
|
||||
}
|
||||
/*
|
||||
* Create private planes and CRTCs for the last NUM_CRTCs overlay
|
||||
* plus manager:
|
||||
*/
|
||||
for (id = 0; id < min(num_crtc, num_ovls); id++) {
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
for (i = 0; i < kms_pdata->dev_cnt; i++) {
|
||||
struct omap_dss_device *dssdev =
|
||||
omap_dss_find_device(
|
||||
(void *)kms_pdata->dev_names[i],
|
||||
match_dev_name);
|
||||
if (!dssdev) {
|
||||
dev_warn(dev->dev, "no such dssdev: %s\n",
|
||||
kms_pdata->dev_names[i]);
|
||||
continue;
|
||||
}
|
||||
create_connector(dev, dssdev);
|
||||
}
|
||||
plane = omap_plane_init(dev, id, true);
|
||||
crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
|
||||
|
||||
connected_connectors = detect_connectors(dev);
|
||||
BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
|
||||
priv->crtcs[id] = crtc;
|
||||
priv->num_crtcs++;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < kms_pdata->ovl_cnt; i++) {
|
||||
struct omap_overlay *ovl =
|
||||
omap_dss_get_overlay(kms_pdata->ovl_ids[i]);
|
||||
create_crtc(dev, ovl, &j, connected_connectors);
|
||||
}
|
||||
|
||||
for (i = 0; i < kms_pdata->pln_cnt; i++) {
|
||||
struct omap_overlay *ovl =
|
||||
omap_dss_get_overlay(kms_pdata->pln_ids[i]);
|
||||
create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
|
||||
}
|
||||
} else {
|
||||
/* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
|
||||
* to make educated guesses about everything else
|
||||
*/
|
||||
int max_overlays = min(omap_dss_get_num_overlays(), num_crtc);
|
||||
|
||||
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++)
|
||||
create_encoder(dev, omap_dss_get_overlay_manager(i));
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
create_connector(dev, dssdev);
|
||||
}
|
||||
|
||||
connected_connectors = detect_connectors(dev);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < max_overlays; i++) {
|
||||
create_crtc(dev, omap_dss_get_overlay(i),
|
||||
&j, connected_connectors);
|
||||
}
|
||||
|
||||
/* use any remaining overlays as drm planes */
|
||||
for (; i < omap_dss_get_num_overlays(); i++) {
|
||||
struct omap_overlay *ovl = omap_dss_get_overlay(i);
|
||||
create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
|
||||
}
|
||||
priv->planes[id] = plane;
|
||||
priv->num_planes++;
|
||||
}
|
||||
|
||||
/* for now keep the mapping of CRTCs and encoders static.. */
|
||||
for (i = 0; i < priv->num_encoders; i++) {
|
||||
struct drm_encoder *encoder = priv->encoders[i];
|
||||
struct omap_overlay_manager *mgr =
|
||||
omap_encoder_get_manager(encoder);
|
||||
/*
|
||||
* Create normal planes for the remaining overlays:
|
||||
*/
|
||||
for (; id < num_ovls; id++) {
|
||||
struct drm_plane *plane = omap_plane_init(dev, id, false);
|
||||
|
||||
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
||||
|
||||
DBG("%s: possible_crtcs=%08x", mgr->name,
|
||||
encoder->possible_crtcs);
|
||||
BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
|
||||
priv->planes[priv->num_planes++] = plane;
|
||||
}
|
||||
|
||||
dump_video_chains();
|
||||
for_each_dss_dev(dssdev) {
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
if (!dssdev->driver) {
|
||||
dev_warn(dev->dev, "%s has no driver.. skipping it\n",
|
||||
dssdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dssdev->driver->get_timings ||
|
||||
dssdev->driver->read_edid)) {
|
||||
dev_warn(dev->dev, "%s driver does not support "
|
||||
"get_timings or read_edid.. skipping it!\n",
|
||||
dssdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
encoder = omap_encoder_init(dev, dssdev);
|
||||
|
||||
if (!encoder) {
|
||||
dev_err(dev->dev, "could not create encoder: %s\n",
|
||||
dssdev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
connector = omap_connector_init(dev,
|
||||
get_connector_type(dssdev), dssdev, encoder);
|
||||
|
||||
if (!connector) {
|
||||
dev_err(dev->dev, "could not create connector: %s\n",
|
||||
dssdev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
|
||||
BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
priv->connectors[priv->num_connectors++] = connector;
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
/* figure out which crtc's we can connect the encoder to: */
|
||||
encoder->possible_crtcs = 0;
|
||||
for (id = 0; id < priv->num_crtcs; id++) {
|
||||
enum omap_dss_output_id supported_outputs =
|
||||
dss_feat_get_supported_outputs(pipe2chan(id));
|
||||
if (supported_outputs & dssdev->output->id)
|
||||
encoder->possible_crtcs |= (1 << id);
|
||||
}
|
||||
}
|
||||
|
||||
dev->mode_config.min_width = 32;
|
||||
dev->mode_config.min_height = 32;
|
||||
|
@ -450,7 +229,7 @@ static int ioctl_gem_new(struct drm_device *dev, void *data,
|
|||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_omap_gem_new *args = data;
|
||||
DBG("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
|
||||
VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
|
||||
args->size.bytes, args->flags);
|
||||
return omap_gem_new_handle(dev, file_priv, args->size,
|
||||
args->flags, &args->handle);
|
||||
|
@ -510,7 +289,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data,
|
|||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
DBG("%p:%p: handle=%d", dev, file_priv, args->handle);
|
||||
VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
|
||||
|
||||
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
|
||||
if (!obj)
|
||||
|
@ -565,14 +344,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
|
|||
|
||||
dev->dev_private = priv;
|
||||
|
||||
ret = omapdss_compat_init();
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "coult not init omapdss\n");
|
||||
dev->dev_private = NULL;
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
|
||||
|
||||
INIT_LIST_HEAD(&priv->obj_list);
|
||||
|
@ -584,10 +355,13 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
|
|||
dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
|
||||
dev->dev_private = NULL;
|
||||
kfree(priv);
|
||||
omapdss_compat_uninit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_vblank_init(dev, priv->num_crtcs);
|
||||
if (ret)
|
||||
dev_warn(dev->dev, "could not init vblank\n");
|
||||
|
||||
priv->fbdev = omap_fbdev_init(dev);
|
||||
if (!priv->fbdev) {
|
||||
dev_warn(dev->dev, "omap_fbdev_init failed\n");
|
||||
|
@ -596,10 +370,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
|
|||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
ret = drm_vblank_init(dev, priv->num_crtcs);
|
||||
if (ret)
|
||||
dev_warn(dev->dev, "could not init vblank\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -609,8 +379,9 @@ static int dev_unload(struct drm_device *dev)
|
|||
|
||||
DBG("unload: dev=%p", dev);
|
||||
|
||||
drm_vblank_cleanup(dev);
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
omap_drm_irq_uninstall(dev);
|
||||
|
||||
omap_fbdev_free(dev);
|
||||
omap_modeset_free(dev);
|
||||
|
@ -619,8 +390,6 @@ static int dev_unload(struct drm_device *dev)
|
|||
flush_workqueue(priv->wq);
|
||||
destroy_workqueue(priv->wq);
|
||||
|
||||
omapdss_compat_uninit();
|
||||
|
||||
kfree(dev->dev_private);
|
||||
dev->dev_private = NULL;
|
||||
|
||||
|
@ -680,7 +449,9 @@ static void dev_lastclose(struct drm_device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
if (ret)
|
||||
DBG("failed to restore crtc mode");
|
||||
}
|
||||
|
@ -695,60 +466,6 @@ static void dev_postclose(struct drm_device *dev, struct drm_file *file)
|
|||
DBG("postclose: dev=%p, file=%p", dev, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* enable_vblank - enable vblank interrupt events
|
||||
* @dev: DRM device
|
||||
* @crtc: which irq to enable
|
||||
*
|
||||
* Enable vblank interrupts for @crtc. If the device doesn't have
|
||||
* a hardware vblank counter, this routine should be a no-op, since
|
||||
* interrupts will have to stay on to keep the count accurate.
|
||||
*
|
||||
* RETURNS
|
||||
* Zero on success, appropriate errno if the given @crtc's vblank
|
||||
* interrupt cannot be enabled.
|
||||
*/
|
||||
static int dev_enable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable_vblank - disable vblank interrupt events
|
||||
* @dev: DRM device
|
||||
* @crtc: which irq to enable
|
||||
*
|
||||
* Disable vblank interrupts for @crtc. If the device doesn't have
|
||||
* a hardware vblank counter, this routine should be a no-op, since
|
||||
* interrupts will have to stay on to keep the count accurate.
|
||||
*/
|
||||
static void dev_disable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
|
||||
}
|
||||
|
||||
static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dev_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
DBG("irq_preinstall: dev=%p", dev);
|
||||
}
|
||||
|
||||
static int dev_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
DBG("irq_postinstall: dev=%p", dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
DBG("irq_uninstall: dev=%p", dev);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct omap_gem_vm_ops = {
|
||||
.fault = omap_gem_fault,
|
||||
.open = drm_gem_vm_open,
|
||||
|
@ -778,12 +495,12 @@ static struct drm_driver omap_drm_driver = {
|
|||
.preclose = dev_preclose,
|
||||
.postclose = dev_postclose,
|
||||
.get_vblank_counter = drm_vblank_count,
|
||||
.enable_vblank = dev_enable_vblank,
|
||||
.disable_vblank = dev_disable_vblank,
|
||||
.irq_preinstall = dev_irq_preinstall,
|
||||
.irq_postinstall = dev_irq_postinstall,
|
||||
.irq_uninstall = dev_irq_uninstall,
|
||||
.irq_handler = dev_irq_handler,
|
||||
.enable_vblank = omap_irq_enable_vblank,
|
||||
.disable_vblank = omap_irq_disable_vblank,
|
||||
.irq_preinstall = omap_irq_preinstall,
|
||||
.irq_postinstall = omap_irq_postinstall,
|
||||
.irq_uninstall = omap_irq_uninstall,
|
||||
.irq_handler = omap_irq_handler,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.debugfs_init = omap_debugfs_init,
|
||||
.debugfs_cleanup = omap_debugfs_cleanup,
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/platform_data/omap_drm.h>
|
||||
#include "omap_drm.h"
|
||||
|
||||
|
||||
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
|
||||
|
||||
|
@ -39,6 +40,51 @@
|
|||
*/
|
||||
#define MAX_MAPPERS 2
|
||||
|
||||
/* parameters which describe (unrotated) coordinates of scanout within a fb: */
|
||||
struct omap_drm_window {
|
||||
uint32_t rotation;
|
||||
int32_t crtc_x, crtc_y; /* signed because can be offscreen */
|
||||
uint32_t crtc_w, crtc_h;
|
||||
uint32_t src_x, src_y;
|
||||
uint32_t src_w, src_h;
|
||||
};
|
||||
|
||||
/* Once GO bit is set, we can't make further updates to shadowed registers
|
||||
* until the GO bit is cleared. So various parts in the kms code that need
|
||||
* to update shadowed registers queue up a pair of callbacks, pre_apply
|
||||
* which is called before setting GO bit, and post_apply that is called
|
||||
* after GO bit is cleared. The crtc manages the queuing, and everyone
|
||||
* else goes thru omap_crtc_apply() using these callbacks so that the
|
||||
* code which has to deal w/ GO bit state is centralized.
|
||||
*/
|
||||
struct omap_drm_apply {
|
||||
struct list_head pending_node, queued_node;
|
||||
bool queued;
|
||||
void (*pre_apply)(struct omap_drm_apply *apply);
|
||||
void (*post_apply)(struct omap_drm_apply *apply);
|
||||
};
|
||||
|
||||
/* For transiently registering for different DSS irqs that various parts
|
||||
* of the KMS code need during setup/configuration. We these are not
|
||||
* necessarily the same as what drm_vblank_get/put() are requesting, and
|
||||
* the hysteresis in drm_vblank_put() is not necessarily desirable for
|
||||
* internal housekeeping related irq usage.
|
||||
*/
|
||||
struct omap_drm_irq {
|
||||
struct list_head node;
|
||||
uint32_t irqmask;
|
||||
bool registered;
|
||||
void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus);
|
||||
};
|
||||
|
||||
/* For KMS code that needs to wait for a certain # of IRQs:
|
||||
*/
|
||||
struct omap_irq_wait;
|
||||
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
|
||||
uint32_t irqmask, int count);
|
||||
int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
|
||||
unsigned long timeout);
|
||||
|
||||
struct omap_drm_private {
|
||||
uint32_t omaprev;
|
||||
|
||||
|
@ -58,6 +104,7 @@ struct omap_drm_private {
|
|||
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/* list of GEM objects: */
|
||||
struct list_head obj_list;
|
||||
|
||||
bool has_dmm;
|
||||
|
@ -65,6 +112,11 @@ struct omap_drm_private {
|
|||
/* properties: */
|
||||
struct drm_property *rotation_prop;
|
||||
struct drm_property *zorder_prop;
|
||||
|
||||
/* irq handling: */
|
||||
struct list_head irq_list; /* list of omap_drm_irq */
|
||||
uint32_t vblank_mask; /* irq bits set for userspace vblank */
|
||||
struct omap_drm_irq error_handler;
|
||||
};
|
||||
|
||||
/* this should probably be in drm-core to standardize amongst drivers */
|
||||
|
@ -75,15 +127,6 @@ struct omap_drm_private {
|
|||
#define DRM_REFLECT_X 4
|
||||
#define DRM_REFLECT_Y 5
|
||||
|
||||
/* parameters which describe (unrotated) coordinates of scanout within a fb: */
|
||||
struct omap_drm_window {
|
||||
uint32_t rotation;
|
||||
int32_t crtc_x, crtc_y; /* signed because can be offscreen */
|
||||
uint32_t crtc_w, crtc_h;
|
||||
uint32_t src_x, src_y;
|
||||
uint32_t src_w, src_h;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int omap_debugfs_init(struct drm_minor *minor);
|
||||
void omap_debugfs_cleanup(struct drm_minor *minor);
|
||||
|
@ -92,23 +135,36 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
|
|||
void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||||
#endif
|
||||
|
||||
int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
|
||||
void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
|
||||
irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
|
||||
void omap_irq_preinstall(struct drm_device *dev);
|
||||
int omap_irq_postinstall(struct drm_device *dev);
|
||||
void omap_irq_uninstall(struct drm_device *dev);
|
||||
void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
|
||||
void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
|
||||
int omap_drm_irq_uninstall(struct drm_device *dev);
|
||||
int omap_drm_irq_install(struct drm_device *dev);
|
||||
|
||||
struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
|
||||
void omap_fbdev_free(struct drm_device *dev);
|
||||
|
||||
const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc);
|
||||
enum omap_channel omap_crtc_channel(struct drm_crtc *crtc);
|
||||
int omap_crtc_apply(struct drm_crtc *crtc,
|
||||
struct omap_drm_apply *apply);
|
||||
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
||||
struct omap_overlay *ovl, int id);
|
||||
struct drm_plane *plane, enum omap_channel channel, int id);
|
||||
|
||||
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||||
struct omap_overlay *ovl, unsigned int possible_crtcs,
|
||||
bool priv);
|
||||
int plane_id, bool private_plane);
|
||||
int omap_plane_dpms(struct drm_plane *plane, int mode);
|
||||
int omap_plane_mode_set(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
void omap_plane_on_endwin(struct drm_plane *plane,
|
||||
uint32_t src_w, uint32_t src_h,
|
||||
void (*fxn)(void *), void *arg);
|
||||
void omap_plane_install_properties(struct drm_plane *plane,
|
||||
struct drm_mode_object *obj);
|
||||
|
@ -116,21 +172,25 @@ int omap_plane_set_property(struct drm_plane *plane,
|
|||
struct drm_property *property, uint64_t val);
|
||||
|
||||
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
||||
struct omap_overlay_manager *mgr);
|
||||
struct omap_overlay_manager *omap_encoder_get_manager(
|
||||
struct omap_dss_device *dssdev);
|
||||
int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled);
|
||||
int omap_encoder_update(struct drm_encoder *encoder,
|
||||
struct omap_overlay_manager *mgr,
|
||||
struct omap_video_timings *timings);
|
||||
|
||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
int connector_type, struct omap_dss_device *dssdev,
|
||||
struct drm_encoder *encoder);
|
||||
struct drm_encoder *omap_connector_attached_encoder(
|
||||
struct drm_connector *connector);
|
||||
enum drm_connector_status omap_connector_detect(
|
||||
struct drm_connector *connector, bool force);
|
||||
|
||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
int connector_type, struct omap_dss_device *dssdev);
|
||||
void omap_connector_mode_set(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
void omap_connector_flush(struct drm_connector *connector,
|
||||
int x, int y, int w, int h);
|
||||
|
||||
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
|
||||
struct omap_video_timings *timings);
|
||||
void copy_timings_drm_to_omap(struct omap_video_timings *timings,
|
||||
struct drm_display_mode *mode);
|
||||
|
||||
uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
|
||||
uint32_t max_formats, enum omap_color_mode supported_modes);
|
||||
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
|
||||
|
@ -207,6 +267,40 @@ static inline int align_pitch(int pitch, int width, int bpp)
|
|||
return ALIGN(pitch, 8 * bytespp);
|
||||
}
|
||||
|
||||
static inline enum omap_channel pipe2chan(int pipe)
|
||||
{
|
||||
int num_mgrs = dss_feat_get_num_mgrs();
|
||||
|
||||
/*
|
||||
* We usually don't want to create a CRTC for each manager,
|
||||
* at least not until we have a way to expose private planes
|
||||
* to userspace. Otherwise there would not be enough video
|
||||
* pipes left for drm planes. The higher #'d managers tend
|
||||
* to have more features so start in reverse order.
|
||||
*/
|
||||
return num_mgrs - pipe - 1;
|
||||
}
|
||||
|
||||
/* map crtc to vblank mask */
|
||||
static inline uint32_t pipe2vbl(int crtc)
|
||||
{
|
||||
enum omap_channel channel = pipe2chan(crtc);
|
||||
return dispc_mgr_get_vsync_irq(channel);
|
||||
}
|
||||
|
||||
static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
|
||||
if (priv->crtcs[i] == crtc)
|
||||
return i;
|
||||
|
||||
BUG(); /* bogus CRTC ptr */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* should these be made into common util helpers?
|
||||
*/
|
||||
|
||||
|
|
|
@ -22,37 +22,56 @@
|
|||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
|
||||
/*
|
||||
* encoder funcs
|
||||
*/
|
||||
|
||||
#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
|
||||
|
||||
/* The encoder and connector both map to same dssdev.. the encoder
|
||||
* handles the 'active' parts, ie. anything the modifies the state
|
||||
* of the hw, and the connector handles the 'read-only' parts, like
|
||||
* detecting connection and reading edid.
|
||||
*/
|
||||
struct omap_encoder {
|
||||
struct drm_encoder base;
|
||||
struct omap_overlay_manager *mgr;
|
||||
struct omap_dss_device *dssdev;
|
||||
};
|
||||
|
||||
static void omap_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
DBG("%s", omap_encoder->mgr->name);
|
||||
drm_encoder_cleanup(encoder);
|
||||
kfree(omap_encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs omap_encoder_funcs = {
|
||||
.destroy = omap_encoder_destroy,
|
||||
};
|
||||
|
||||
/*
|
||||
* The CRTC drm_crtc_helper_set_mode() doesn't really give us the right
|
||||
* order.. the easiest way to work around this for now is to make all
|
||||
* the encoder-helper's no-op's and have the omap_crtc code take care
|
||||
* of the sequencing and call us in the right points.
|
||||
*
|
||||
* Eventually to handle connecting CRTCs to different encoders properly,
|
||||
* either the CRTC helpers need to change or we need to replace
|
||||
* drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for
|
||||
* that.
|
||||
*/
|
||||
|
||||
static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
DBG("%s: %d", omap_encoder->mgr->name, mode);
|
||||
}
|
||||
|
||||
static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
DBG("%s", omap_encoder->mgr->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -60,47 +79,16 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
|
|||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
mode = adjusted_mode;
|
||||
|
||||
DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
|
||||
mode->hdisplay, mode->vdisplay);
|
||||
|
||||
for (i = 0; i < priv->num_connectors; i++) {
|
||||
struct drm_connector *connector = priv->connectors[i];
|
||||
if (connector->encoder == encoder)
|
||||
omap_connector_mode_set(connector, mode);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void omap_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
encoder->helper_private;
|
||||
DBG("%s", omap_encoder->mgr->name);
|
||||
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void omap_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
encoder->helper_private;
|
||||
DBG("%s", omap_encoder->mgr->name);
|
||||
omap_encoder->mgr->apply(omap_encoder->mgr);
|
||||
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs omap_encoder_funcs = {
|
||||
.destroy = omap_encoder_destroy,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
|
||||
.dpms = omap_encoder_dpms,
|
||||
.mode_fixup = omap_encoder_mode_fixup,
|
||||
|
@ -109,23 +97,54 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
|
|||
.commit = omap_encoder_commit,
|
||||
};
|
||||
|
||||
struct omap_overlay_manager *omap_encoder_get_manager(
|
||||
struct drm_encoder *encoder)
|
||||
/*
|
||||
* Instead of relying on the helpers for modeset, the omap_crtc code
|
||||
* calls these functions in the proper sequence.
|
||||
*/
|
||||
|
||||
int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
return omap_encoder->mgr;
|
||||
struct omap_dss_device *dssdev = omap_encoder->dssdev;
|
||||
struct omap_dss_driver *dssdrv = dssdev->driver;
|
||||
|
||||
if (enabled) {
|
||||
return dssdrv->enable(dssdev);
|
||||
} else {
|
||||
dssdrv->disable(dssdev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int omap_encoder_update(struct drm_encoder *encoder,
|
||||
struct omap_overlay_manager *mgr,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
struct omap_dss_device *dssdev = omap_encoder->dssdev;
|
||||
struct omap_dss_driver *dssdrv = dssdev->driver;
|
||||
int ret;
|
||||
|
||||
dssdev->output->manager = mgr;
|
||||
|
||||
ret = dssdrv->check_timings(dssdev, timings);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not set timings: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dssdrv->set_timings(dssdev, timings);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initialize encoder */
|
||||
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
||||
struct omap_overlay_manager *mgr)
|
||||
struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct omap_encoder *omap_encoder;
|
||||
struct omap_overlay_manager_info info;
|
||||
int ret;
|
||||
|
||||
DBG("%s", mgr->name);
|
||||
|
||||
omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
|
||||
if (!omap_encoder) {
|
||||
|
@ -133,33 +152,14 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
omap_encoder->mgr = mgr;
|
||||
omap_encoder->dssdev = dssdev;
|
||||
|
||||
encoder = &omap_encoder->base;
|
||||
|
||||
drm_encoder_init(dev, encoder, &omap_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
|
||||
|
||||
mgr->get_manager_info(mgr, &info);
|
||||
|
||||
/* TODO: fix hard-coded setup.. */
|
||||
info.default_color = 0x00000000;
|
||||
info.trans_key = 0x00000000;
|
||||
info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
|
||||
info.trans_enabled = false;
|
||||
|
||||
ret = mgr->set_manager_info(mgr, &info);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not set manager info\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = mgr->apply(mgr);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not apply\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return encoder;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -194,7 +194,7 @@ struct dma_buf_ops omap_dmabuf_ops = {
|
|||
struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, 0600);
|
||||
return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags);
|
||||
}
|
||||
|
||||
struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* drivers/staging/omapdrm/omap_irq.c
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Author: Rob Clark <rob.clark@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "omap_drv.h"
|
||||
|
||||
static DEFINE_SPINLOCK(list_lock);
|
||||
|
||||
static void omap_irq_error_handler(struct omap_drm_irq *irq,
|
||||
uint32_t irqstatus)
|
||||
{
|
||||
DRM_ERROR("errors: %08x\n", irqstatus);
|
||||
}
|
||||
|
||||
/* call with list_lock and dispc runtime held */
|
||||
static void omap_irq_update(struct drm_device *dev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_drm_irq *irq;
|
||||
uint32_t irqmask = priv->vblank_mask;
|
||||
|
||||
BUG_ON(!spin_is_locked(&list_lock));
|
||||
|
||||
list_for_each_entry(irq, &priv->irq_list, node)
|
||||
irqmask |= irq->irqmask;
|
||||
|
||||
DBG("irqmask=%08x", irqmask);
|
||||
|
||||
dispc_write_irqenable(irqmask);
|
||||
dispc_read_irqenable(); /* flush posted write */
|
||||
}
|
||||
|
||||
void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
unsigned long flags;
|
||||
|
||||
dispc_runtime_get();
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
|
||||
if (!WARN_ON(irq->registered)) {
|
||||
irq->registered = true;
|
||||
list_add(&irq->node, &priv->irq_list);
|
||||
omap_irq_update(dev);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dispc_runtime_get();
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
|
||||
if (!WARN_ON(!irq->registered)) {
|
||||
irq->registered = false;
|
||||
list_del(&irq->node);
|
||||
omap_irq_update(dev);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
struct omap_irq_wait {
|
||||
struct omap_drm_irq irq;
|
||||
int count;
|
||||
};
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(wait_event);
|
||||
|
||||
static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct omap_irq_wait *wait =
|
||||
container_of(irq, struct omap_irq_wait, irq);
|
||||
wait->count--;
|
||||
wake_up_all(&wait_event);
|
||||
}
|
||||
|
||||
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
|
||||
uint32_t irqmask, int count)
|
||||
{
|
||||
struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
|
||||
wait->irq.irq = wait_irq;
|
||||
wait->irq.irqmask = irqmask;
|
||||
wait->count = count;
|
||||
omap_irq_register(dev, &wait->irq);
|
||||
return wait;
|
||||
}
|
||||
|
||||
int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
|
||||
unsigned long timeout)
|
||||
{
|
||||
int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
|
||||
omap_irq_unregister(dev, &wait->irq);
|
||||
kfree(wait);
|
||||
if (ret == 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* enable_vblank - enable vblank interrupt events
|
||||
* @dev: DRM device
|
||||
* @crtc: which irq to enable
|
||||
*
|
||||
* Enable vblank interrupts for @crtc. If the device doesn't have
|
||||
* a hardware vblank counter, this routine should be a no-op, since
|
||||
* interrupts will have to stay on to keep the count accurate.
|
||||
*
|
||||
* RETURNS
|
||||
* Zero on success, appropriate errno if the given @crtc's vblank
|
||||
* interrupt cannot be enabled.
|
||||
*/
|
||||
int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
unsigned long flags;
|
||||
|
||||
DBG("dev=%p, crtc=%d", dev, crtc);
|
||||
|
||||
dispc_runtime_get();
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
priv->vblank_mask |= pipe2vbl(crtc);
|
||||
omap_irq_update(dev);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
dispc_runtime_put();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable_vblank - disable vblank interrupt events
|
||||
* @dev: DRM device
|
||||
* @crtc: which irq to enable
|
||||
*
|
||||
* Disable vblank interrupts for @crtc. If the device doesn't have
|
||||
* a hardware vblank counter, this routine should be a no-op, since
|
||||
* interrupts will have to stay on to keep the count accurate.
|
||||
*/
|
||||
void omap_irq_disable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
unsigned long flags;
|
||||
|
||||
DBG("dev=%p, crtc=%d", dev, crtc);
|
||||
|
||||
dispc_runtime_get();
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
priv->vblank_mask &= ~pipe2vbl(crtc);
|
||||
omap_irq_update(dev);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
irqreturn_t omap_irq_handler(DRM_IRQ_ARGS)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_drm_irq *handler, *n;
|
||||
unsigned long flags;
|
||||
unsigned int id;
|
||||
u32 irqstatus;
|
||||
|
||||
irqstatus = dispc_read_irqstatus();
|
||||
dispc_clear_irqstatus(irqstatus);
|
||||
dispc_read_irqstatus(); /* flush posted write */
|
||||
|
||||
VERB("irqs: %08x", irqstatus);
|
||||
|
||||
for (id = 0; id < priv->num_crtcs; id++)
|
||||
if (irqstatus & pipe2vbl(id))
|
||||
drm_handle_vblank(dev, id);
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
list_for_each_entry_safe(handler, n, &priv->irq_list, node) {
|
||||
if (handler->irqmask & irqstatus) {
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
handler->irq(handler, handler->irqmask & irqstatus);
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void omap_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
DBG("dev=%p", dev);
|
||||
dispc_runtime_get();
|
||||
dispc_clear_irqstatus(0xffffffff);
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
int omap_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_drm_irq *error_handler = &priv->error_handler;
|
||||
|
||||
DBG("dev=%p", dev);
|
||||
|
||||
INIT_LIST_HEAD(&priv->irq_list);
|
||||
|
||||
error_handler->irq = omap_irq_error_handler;
|
||||
error_handler->irqmask = DISPC_IRQ_OCP_ERR;
|
||||
|
||||
/* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
|
||||
* we just need to ignore it while enabling tv-out
|
||||
*/
|
||||
error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
|
||||
|
||||
omap_irq_register(dev, error_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
DBG("dev=%p", dev);
|
||||
// TODO prolly need to call drm_irq_uninstall() somewhere too
|
||||
}
|
||||
|
||||
/*
|
||||
* We need a special version, instead of just using drm_irq_install(),
|
||||
* because we need to register the irq via omapdss. Once omapdss and
|
||||
* omapdrm are merged together we can assign the dispc hwmod data to
|
||||
* ourselves and drop these and just use drm_irq_{install,uninstall}()
|
||||
*/
|
||||
|
||||
int omap_drm_irq_install(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
if (dev->irq_enabled) {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
dev->irq_enabled = 1;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
/* Before installing handler */
|
||||
if (dev->driver->irq_preinstall)
|
||||
dev->driver->irq_preinstall(dev);
|
||||
|
||||
ret = dispc_request_irq(dev->driver->irq_handler, dev);
|
||||
|
||||
if (ret < 0) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
dev->irq_enabled = 0;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* After installing handler */
|
||||
if (dev->driver->irq_postinstall)
|
||||
ret = dev->driver->irq_postinstall(dev);
|
||||
|
||||
if (ret < 0) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
dev->irq_enabled = 0;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
dispc_free_irq(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omap_drm_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
int irq_enabled, i;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
irq_enabled = dev->irq_enabled;
|
||||
dev->irq_enabled = 0;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
/*
|
||||
* Wake up any waiters so they don't hang.
|
||||
*/
|
||||
if (dev->num_crtcs) {
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
for (i = 0; i < dev->num_crtcs; i++) {
|
||||
DRM_WAKEUP(&dev->vbl_queue[i]);
|
||||
dev->vblank_enabled[i] = 0;
|
||||
dev->last_vblank[i] =
|
||||
dev->driver->get_vblank_counter(dev, i);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
}
|
||||
|
||||
if (!irq_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->driver->irq_uninstall)
|
||||
dev->driver->irq_uninstall(dev);
|
||||
|
||||
dispc_free_irq(dev);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -41,12 +41,14 @@ struct callback {
|
|||
|
||||
struct omap_plane {
|
||||
struct drm_plane base;
|
||||
struct omap_overlay *ovl;
|
||||
int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
|
||||
const char *name;
|
||||
struct omap_overlay_info info;
|
||||
struct omap_drm_apply apply;
|
||||
|
||||
/* position/orientation of scanout within the fb: */
|
||||
struct omap_drm_window win;
|
||||
|
||||
bool enabled;
|
||||
|
||||
/* last fb that we pinned: */
|
||||
struct drm_framebuffer *pinned_fb;
|
||||
|
@ -54,189 +56,15 @@ struct omap_plane {
|
|||
uint32_t nformats;
|
||||
uint32_t formats[32];
|
||||
|
||||
/* for synchronizing access to unpins fifo */
|
||||
struct mutex unpin_mutex;
|
||||
struct omap_drm_irq error_irq;
|
||||
|
||||
/* set of bo's pending unpin until next END_WIN irq */
|
||||
/* set of bo's pending unpin until next post_apply() */
|
||||
DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
|
||||
int num_unpins, pending_num_unpins;
|
||||
|
||||
/* for deferred unpin when we need to wait for scanout complete irq */
|
||||
struct work_struct work;
|
||||
|
||||
/* callback on next endwin irq */
|
||||
struct callback endwin;
|
||||
// XXX maybe get rid of this and handle vblank in crtc too?
|
||||
struct callback apply_done_cb;
|
||||
};
|
||||
|
||||
/* map from ovl->id to the irq we are interested in for scanout-done */
|
||||
static const uint32_t id2irq[] = {
|
||||
[OMAP_DSS_GFX] = DISPC_IRQ_GFX_END_WIN,
|
||||
[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN,
|
||||
[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN,
|
||||
[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN,
|
||||
};
|
||||
|
||||
static void dispc_isr(void *arg, uint32_t mask)
|
||||
{
|
||||
struct drm_plane *plane = arg;
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_drm_private *priv = plane->dev->dev_private;
|
||||
|
||||
omap_dispc_unregister_isr(dispc_isr, plane,
|
||||
id2irq[omap_plane->ovl->id]);
|
||||
|
||||
queue_work(priv->wq, &omap_plane->work);
|
||||
}
|
||||
|
||||
static void unpin_worker(struct work_struct *work)
|
||||
{
|
||||
struct omap_plane *omap_plane =
|
||||
container_of(work, struct omap_plane, work);
|
||||
struct callback endwin;
|
||||
|
||||
mutex_lock(&omap_plane->unpin_mutex);
|
||||
DBG("unpinning %d of %d", omap_plane->num_unpins,
|
||||
omap_plane->num_unpins + omap_plane->pending_num_unpins);
|
||||
while (omap_plane->num_unpins > 0) {
|
||||
struct drm_gem_object *bo = NULL;
|
||||
int ret = kfifo_get(&omap_plane->unpin_fifo, &bo);
|
||||
WARN_ON(!ret);
|
||||
omap_gem_put_paddr(bo);
|
||||
drm_gem_object_unreference_unlocked(bo);
|
||||
omap_plane->num_unpins--;
|
||||
}
|
||||
endwin = omap_plane->endwin;
|
||||
omap_plane->endwin.fxn = NULL;
|
||||
mutex_unlock(&omap_plane->unpin_mutex);
|
||||
|
||||
if (endwin.fxn)
|
||||
endwin.fxn(endwin.arg);
|
||||
}
|
||||
|
||||
static void install_irq(struct drm_plane *plane)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
int ret;
|
||||
|
||||
ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]);
|
||||
|
||||
/*
|
||||
* omapdss has upper limit on # of registered irq handlers,
|
||||
* which we shouldn't hit.. but if we do the limit should
|
||||
* be raised or bad things happen:
|
||||
*/
|
||||
WARN_ON(ret == -EBUSY);
|
||||
}
|
||||
|
||||
/* push changes down to dss2 */
|
||||
static int commit(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
struct omap_overlay_info *info = &omap_plane->info;
|
||||
int ret;
|
||||
|
||||
DBG("%s", ovl->name);
|
||||
DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
|
||||
info->out_height, info->screen_width);
|
||||
DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
|
||||
info->paddr, info->p_uv_addr);
|
||||
|
||||
/* NOTE: do we want to do this at all here, or just wait
|
||||
* for dpms(ON) since other CRTC's may not have their mode
|
||||
* set yet, so fb dimensions may still change..
|
||||
*/
|
||||
ret = ovl->set_overlay_info(ovl, info);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not set overlay info\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&omap_plane->unpin_mutex);
|
||||
omap_plane->num_unpins += omap_plane->pending_num_unpins;
|
||||
omap_plane->pending_num_unpins = 0;
|
||||
mutex_unlock(&omap_plane->unpin_mutex);
|
||||
|
||||
/* our encoder doesn't necessarily get a commit() after this, in
|
||||
* particular in the dpms() and mode_set_base() cases, so force the
|
||||
* manager to update:
|
||||
*
|
||||
* could this be in the encoder somehow?
|
||||
*/
|
||||
if (ovl->manager) {
|
||||
ret = ovl->manager->apply(ovl->manager);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not apply settings\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: really this should be atomic w/ mgr->apply() but
|
||||
* omapdss does not expose such an API
|
||||
*/
|
||||
if (omap_plane->num_unpins > 0)
|
||||
install_irq(plane);
|
||||
|
||||
} else {
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
queue_work(priv->wq, &omap_plane->work);
|
||||
}
|
||||
|
||||
|
||||
if (ovl->is_enabled(ovl)) {
|
||||
omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
|
||||
info->out_width, info->out_height);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* when CRTC that we are attached to has potentially changed, this checks
|
||||
* if we are attached to proper manager, and if necessary updates.
|
||||
*/
|
||||
static void update_manager(struct drm_plane *plane)
|
||||
{
|
||||
struct omap_drm_private *priv = plane->dev->dev_private;
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
struct omap_overlay_manager *mgr = NULL;
|
||||
int i;
|
||||
|
||||
if (plane->crtc) {
|
||||
for (i = 0; i < priv->num_encoders; i++) {
|
||||
struct drm_encoder *encoder = priv->encoders[i];
|
||||
if (encoder->crtc == plane->crtc) {
|
||||
mgr = omap_encoder_get_manager(encoder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ovl->manager != mgr) {
|
||||
bool enabled = ovl->is_enabled(ovl);
|
||||
|
||||
/* don't switch things around with enabled overlays: */
|
||||
if (enabled)
|
||||
omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
|
||||
|
||||
if (ovl->manager) {
|
||||
DBG("disconnecting %s from %s", ovl->name,
|
||||
ovl->manager->name);
|
||||
ovl->unset_manager(ovl);
|
||||
}
|
||||
|
||||
if (mgr) {
|
||||
DBG("connecting %s to %s", ovl->name, mgr->name);
|
||||
ovl->set_manager(ovl, mgr);
|
||||
}
|
||||
|
||||
if (enabled && mgr)
|
||||
omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
}
|
||||
|
||||
static void unpin(void *arg, struct drm_gem_object *bo)
|
||||
{
|
||||
struct drm_plane *plane = arg;
|
||||
|
@ -244,7 +72,6 @@ static void unpin(void *arg, struct drm_gem_object *bo)
|
|||
|
||||
if (kfifo_put(&omap_plane->unpin_fifo,
|
||||
(const struct drm_gem_object **)&bo)) {
|
||||
omap_plane->pending_num_unpins++;
|
||||
/* also hold a ref so it isn't free'd while pinned */
|
||||
drm_gem_object_reference(bo);
|
||||
} else {
|
||||
|
@ -264,13 +91,19 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
|
|||
|
||||
DBG("%p -> %p", pinned_fb, fb);
|
||||
|
||||
mutex_lock(&omap_plane->unpin_mutex);
|
||||
if (fb)
|
||||
drm_framebuffer_reference(fb);
|
||||
|
||||
ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
|
||||
mutex_unlock(&omap_plane->unpin_mutex);
|
||||
|
||||
if (pinned_fb)
|
||||
drm_framebuffer_unreference(pinned_fb);
|
||||
|
||||
if (ret) {
|
||||
dev_err(plane->dev->dev, "could not swap %p -> %p\n",
|
||||
omap_plane->pinned_fb, fb);
|
||||
if (fb)
|
||||
drm_framebuffer_unreference(fb);
|
||||
omap_plane->pinned_fb = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
@ -281,31 +114,90 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* update parameters that are dependent on the framebuffer dimensions and
|
||||
* position within the fb that this plane scans out from. This is called
|
||||
* when framebuffer or x,y base may have changed.
|
||||
*/
|
||||
static void update_scanout(struct drm_plane *plane)
|
||||
static void omap_plane_pre_apply(struct omap_drm_apply *apply)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_overlay_info *info = &omap_plane->info;
|
||||
struct omap_plane *omap_plane =
|
||||
container_of(apply, struct omap_plane, apply);
|
||||
struct omap_drm_window *win = &omap_plane->win;
|
||||
struct drm_plane *plane = &omap_plane->base;
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct omap_overlay_info *info = &omap_plane->info;
|
||||
struct drm_crtc *crtc = plane->crtc;
|
||||
enum omap_channel channel;
|
||||
bool enabled = omap_plane->enabled && crtc;
|
||||
bool ilace, replication;
|
||||
int ret;
|
||||
|
||||
ret = update_pin(plane, plane->fb);
|
||||
if (ret) {
|
||||
dev_err(plane->dev->dev,
|
||||
"could not pin fb: %d\n", ret);
|
||||
omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
|
||||
DBG("%s, enabled=%d", omap_plane->name, enabled);
|
||||
|
||||
/* if fb has changed, pin new fb: */
|
||||
update_pin(plane, enabled ? plane->fb : NULL);
|
||||
|
||||
if (!enabled) {
|
||||
dispc_ovl_enable(omap_plane->id, false);
|
||||
return;
|
||||
}
|
||||
|
||||
channel = omap_crtc_channel(crtc);
|
||||
|
||||
/* update scanout: */
|
||||
omap_framebuffer_update_scanout(plane->fb, win, info);
|
||||
|
||||
DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
|
||||
win->src_x, win->src_y,
|
||||
(u32)info->paddr, (u32)info->p_uv_addr,
|
||||
DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
|
||||
info->out_width, info->out_height,
|
||||
info->screen_width);
|
||||
DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
|
||||
info->paddr, info->p_uv_addr);
|
||||
|
||||
/* TODO: */
|
||||
ilace = false;
|
||||
replication = false;
|
||||
|
||||
/* and finally, update omapdss: */
|
||||
ret = dispc_ovl_setup(omap_plane->id, info,
|
||||
replication, omap_crtc_timings(crtc), false);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
dispc_ovl_enable(omap_plane->id, true);
|
||||
dispc_ovl_set_channel_out(omap_plane->id, channel);
|
||||
}
|
||||
|
||||
static void omap_plane_post_apply(struct omap_drm_apply *apply)
|
||||
{
|
||||
struct omap_plane *omap_plane =
|
||||
container_of(apply, struct omap_plane, apply);
|
||||
struct drm_plane *plane = &omap_plane->base;
|
||||
struct omap_overlay_info *info = &omap_plane->info;
|
||||
struct drm_gem_object *bo = NULL;
|
||||
struct callback cb;
|
||||
|
||||
cb = omap_plane->apply_done_cb;
|
||||
omap_plane->apply_done_cb.fxn = NULL;
|
||||
|
||||
while (kfifo_get(&omap_plane->unpin_fifo, &bo)) {
|
||||
omap_gem_put_paddr(bo);
|
||||
drm_gem_object_unreference_unlocked(bo);
|
||||
}
|
||||
|
||||
if (cb.fxn)
|
||||
cb.fxn(cb.arg);
|
||||
|
||||
if (omap_plane->enabled) {
|
||||
omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
|
||||
info->out_width, info->out_height);
|
||||
}
|
||||
}
|
||||
|
||||
static int apply(struct drm_plane *plane)
|
||||
{
|
||||
if (plane->crtc) {
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
return omap_crtc_apply(plane->crtc, &omap_plane->apply);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap_plane_mode_set(struct drm_plane *plane,
|
||||
|
@ -313,7 +205,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
|
|||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
uint32_t src_w, uint32_t src_h,
|
||||
void (*fxn)(void *), void *arg)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_drm_window *win = &omap_plane->win;
|
||||
|
@ -329,17 +222,20 @@ int omap_plane_mode_set(struct drm_plane *plane,
|
|||
win->src_w = src_w >> 16;
|
||||
win->src_h = src_h >> 16;
|
||||
|
||||
/* note: this is done after this fxn returns.. but if we need
|
||||
* to do a commit/update_scanout, etc before this returns we
|
||||
* need the current value.
|
||||
*/
|
||||
if (fxn) {
|
||||
/* omap_crtc should ensure that a new page flip
|
||||
* isn't permitted while there is one pending:
|
||||
*/
|
||||
BUG_ON(omap_plane->apply_done_cb.fxn);
|
||||
|
||||
omap_plane->apply_done_cb.fxn = fxn;
|
||||
omap_plane->apply_done_cb.arg = arg;
|
||||
}
|
||||
|
||||
plane->fb = fb;
|
||||
plane->crtc = crtc;
|
||||
|
||||
update_scanout(plane);
|
||||
update_manager(plane);
|
||||
|
||||
return 0;
|
||||
return apply(plane);
|
||||
}
|
||||
|
||||
static int omap_plane_update(struct drm_plane *plane,
|
||||
|
@ -349,9 +245,12 @@ static int omap_plane_update(struct drm_plane *plane,
|
|||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
src_x, src_y, src_w, src_h);
|
||||
return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
omap_plane->enabled = true;
|
||||
return omap_plane_mode_set(plane, crtc, fb,
|
||||
crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
src_x, src_y, src_w, src_h,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static int omap_plane_disable(struct drm_plane *plane)
|
||||
|
@ -364,48 +263,32 @@ static int omap_plane_disable(struct drm_plane *plane)
|
|||
static void omap_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
DBG("%s", omap_plane->ovl->name);
|
||||
|
||||
DBG("%s", omap_plane->name);
|
||||
|
||||
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
|
||||
|
||||
omap_plane_disable(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0);
|
||||
|
||||
WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo));
|
||||
kfifo_free(&omap_plane->unpin_fifo);
|
||||
|
||||
kfree(omap_plane);
|
||||
}
|
||||
|
||||
int omap_plane_dpms(struct drm_plane *plane, int mode)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
int r;
|
||||
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||
int ret = 0;
|
||||
|
||||
DBG("%s: %d", omap_plane->ovl->name, mode);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
update_scanout(plane);
|
||||
r = commit(plane);
|
||||
if (!r)
|
||||
r = ovl->enable(ovl);
|
||||
} else {
|
||||
struct omap_drm_private *priv = plane->dev->dev_private;
|
||||
r = ovl->disable(ovl);
|
||||
update_pin(plane, NULL);
|
||||
queue_work(priv->wq, &omap_plane->work);
|
||||
if (enabled != omap_plane->enabled) {
|
||||
omap_plane->enabled = enabled;
|
||||
ret = apply(plane);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void omap_plane_on_endwin(struct drm_plane *plane,
|
||||
void (*fxn)(void *), void *arg)
|
||||
{
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
|
||||
mutex_lock(&omap_plane->unpin_mutex);
|
||||
omap_plane->endwin.fxn = fxn;
|
||||
omap_plane->endwin.arg = arg;
|
||||
mutex_unlock(&omap_plane->unpin_mutex);
|
||||
|
||||
install_irq(plane);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* helper to install properties which are common to planes and crtcs */
|
||||
|
@ -454,25 +337,13 @@ int omap_plane_set_property(struct drm_plane *plane,
|
|||
int ret = -EINVAL;
|
||||
|
||||
if (property == priv->rotation_prop) {
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
|
||||
DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
|
||||
DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
|
||||
omap_plane->win.rotation = val;
|
||||
|
||||
if (ovl->is_enabled(ovl))
|
||||
ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
||||
else
|
||||
ret = 0;
|
||||
ret = apply(plane);
|
||||
} else if (property == priv->zorder_prop) {
|
||||
struct omap_overlay *ovl = omap_plane->ovl;
|
||||
|
||||
DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
|
||||
DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
|
||||
omap_plane->info.zorder = val;
|
||||
|
||||
if (ovl->is_enabled(ovl))
|
||||
ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
||||
else
|
||||
ret = 0;
|
||||
ret = apply(plane);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -485,20 +356,38 @@ static const struct drm_plane_funcs omap_plane_funcs = {
|
|||
.set_property = omap_plane_set_property,
|
||||
};
|
||||
|
||||
static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct omap_plane *omap_plane =
|
||||
container_of(irq, struct omap_plane, error_irq);
|
||||
DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
|
||||
}
|
||||
|
||||
static const char *plane_names[] = {
|
||||
[OMAP_DSS_GFX] = "gfx",
|
||||
[OMAP_DSS_VIDEO1] = "vid1",
|
||||
[OMAP_DSS_VIDEO2] = "vid2",
|
||||
[OMAP_DSS_VIDEO3] = "vid3",
|
||||
};
|
||||
|
||||
static const uint32_t error_irqs[] = {
|
||||
[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
|
||||
[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
|
||||
[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
|
||||
[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
|
||||
};
|
||||
|
||||
/* initialize plane */
|
||||
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||||
struct omap_overlay *ovl, unsigned int possible_crtcs,
|
||||
bool priv)
|
||||
int id, bool private_plane)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct drm_plane *plane = NULL;
|
||||
struct omap_plane *omap_plane;
|
||||
struct omap_overlay_info *info;
|
||||
int ret;
|
||||
|
||||
DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
|
||||
possible_crtcs, priv);
|
||||
|
||||
/* friendly reminder to update table for future hw: */
|
||||
WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
|
||||
DBG("%s: priv=%d", plane_names[id], private_plane);
|
||||
|
||||
omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
|
||||
if (!omap_plane) {
|
||||
|
@ -506,47 +395,50 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
mutex_init(&omap_plane->unpin_mutex);
|
||||
|
||||
ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not allocate unpin FIFO\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
INIT_WORK(&omap_plane->work, unpin_worker);
|
||||
|
||||
omap_plane->nformats = omap_framebuffer_get_formats(
|
||||
omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
|
||||
ovl->supported_modes);
|
||||
omap_plane->ovl = ovl;
|
||||
dss_feat_get_supported_color_modes(id));
|
||||
omap_plane->id = id;
|
||||
omap_plane->name = plane_names[id];
|
||||
|
||||
plane = &omap_plane->base;
|
||||
|
||||
drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
|
||||
omap_plane->formats, omap_plane->nformats, priv);
|
||||
omap_plane->apply.pre_apply = omap_plane_pre_apply;
|
||||
omap_plane->apply.post_apply = omap_plane_post_apply;
|
||||
|
||||
omap_plane->error_irq.irqmask = error_irqs[id];
|
||||
omap_plane->error_irq.irq = omap_plane_error_irq;
|
||||
omap_irq_register(dev, &omap_plane->error_irq);
|
||||
|
||||
drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
|
||||
omap_plane->formats, omap_plane->nformats, private_plane);
|
||||
|
||||
omap_plane_install_properties(plane, &plane->base);
|
||||
|
||||
/* get our starting configuration, set defaults for parameters
|
||||
* we don't currently use, etc:
|
||||
*/
|
||||
ovl->get_overlay_info(ovl, &omap_plane->info);
|
||||
omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
|
||||
omap_plane->info.rotation = OMAP_DSS_ROT_0;
|
||||
omap_plane->info.global_alpha = 0xff;
|
||||
omap_plane->info.mirror = 0;
|
||||
info = &omap_plane->info;
|
||||
info->rotation_type = OMAP_DSS_ROT_DMA;
|
||||
info->rotation = OMAP_DSS_ROT_0;
|
||||
info->global_alpha = 0xff;
|
||||
info->mirror = 0;
|
||||
|
||||
/* Set defaults depending on whether we are a CRTC or overlay
|
||||
* layer.
|
||||
* TODO add ioctl to give userspace an API to change this.. this
|
||||
* will come in a subsequent patch.
|
||||
*/
|
||||
if (priv)
|
||||
if (private_plane)
|
||||
omap_plane->info.zorder = 0;
|
||||
else
|
||||
omap_plane->info.zorder = ovl->id;
|
||||
|
||||
update_manager(plane);
|
||||
omap_plane->info.zorder = id;
|
||||
|
||||
return plane;
|
||||
|
||||
|
|
|
@ -937,7 +937,8 @@ short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count)
|
|||
|
||||
dma_tmp = pci_map_single(pdev, buf, bufsize * sizeof(u8),
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (pci_dma_mapping_error(pdev, dma_tmp))
|
||||
return -1;
|
||||
if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp,
|
||||
&(priv->rxbufferhead))) {
|
||||
DMESGE("Unable to allocate mem RX buf");
|
||||
|
|
|
@ -1183,6 +1183,8 @@ void rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc,
|
|||
pTxFwInfo->TxRate,
|
||||
cb_desc);
|
||||
|
||||
if (pci_dma_mapping_error(priv->pdev, mapping))
|
||||
RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
|
||||
if (cb_desc->bAMPDUEnable) {
|
||||
pTxFwInfo->AllowAggregation = 1;
|
||||
pTxFwInfo->RxMF = cb_desc->ampdu_factor;
|
||||
|
@ -1280,6 +1282,8 @@ void rtl8192_tx_fill_cmd_desc(struct net_device *dev,
|
|||
dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
|
||||
PCI_DMA_TODEVICE);
|
||||
|
||||
if (pci_dma_mapping_error(priv->pdev, mapping))
|
||||
RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
|
||||
memset(entry, 0, 12);
|
||||
entry->LINIP = cb_desc->bLastIniPkt;
|
||||
entry->FirstSeg = 1;
|
||||
|
|
|
@ -2104,7 +2104,10 @@ static short rtl8192_alloc_rx_desc_ring(struct net_device *dev)
|
|||
skb_tail_pointer_rsl(skb),
|
||||
priv->rxbuffersize,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (pci_dma_mapping_error(priv->pdev, *mapping)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return -1;
|
||||
}
|
||||
entry->BufferAddress = cpu_to_le32(*mapping);
|
||||
|
||||
entry->Length = priv->rxbuffersize;
|
||||
|
@ -2397,7 +2400,11 @@ static void rtl8192_rx_normal(struct net_device *dev)
|
|||
skb_tail_pointer_rsl(skb),
|
||||
priv->rxbuffersize,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
if (pci_dma_mapping_error(priv->pdev,
|
||||
*((dma_addr_t *)skb->cb))) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
done:
|
||||
pdesc->BufferAddress = cpu_to_le32(*((dma_addr_t *)skb->cb));
|
||||
|
|
|
@ -63,6 +63,8 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = {
|
|||
{USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */
|
||||
/* Belkin */
|
||||
{USB_DEVICE(0x050D, 0x945A)},
|
||||
/* ISY IWL - Belkin clone */
|
||||
{USB_DEVICE(0x050D, 0x11F1)},
|
||||
/* Corega */
|
||||
{USB_DEVICE(0x07AA, 0x0047)},
|
||||
/* D-Link */
|
||||
|
|
|
@ -2,6 +2,7 @@ config SB105X
|
|||
tristate "SystemBase PCI Multiport UART"
|
||||
select SERIAL_CORE
|
||||
depends on PCI
|
||||
depends on X86
|
||||
help
|
||||
A driver for the SystemBase Multi-2/PCI serial card
|
||||
|
||||
|
|
|
@ -3054,6 +3054,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
|
|||
sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16);
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_PARPORT
|
||||
case PCI_DEVICE_ID_MP2S1P :
|
||||
sbdev->nr_ports = 2;
|
||||
|
||||
|
@ -3073,6 +3074,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
|
|||
/* add PC compatible parallel port */
|
||||
parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name);
|
||||
|
|
|
@ -342,7 +342,7 @@ int synth_init(char *synth_name)
|
|||
|
||||
mutex_lock(&spk_mutex);
|
||||
/* First, check if we already have it loaded. */
|
||||
for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++)
|
||||
for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
|
||||
if (strcmp(synths[i]->name, synth_name) == 0)
|
||||
synth = synths[i];
|
||||
|
||||
|
@ -423,7 +423,7 @@ int synth_add(struct spk_synth *in_synth)
|
|||
int i;
|
||||
int status = 0;
|
||||
mutex_lock(&spk_mutex);
|
||||
for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++)
|
||||
for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
|
||||
/* synth_remove() is responsible for rotating the array down */
|
||||
if (in_synth == synths[i]) {
|
||||
mutex_unlock(&spk_mutex);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* driver should read or write to PRM/CM registers directly; they
|
||||
* should rely on OMAP core code to do this.
|
||||
*/
|
||||
#include <mach-omap2/cm2xxx_3xxx.h>
|
||||
#include <mach-omap2/cm3xxx.h>
|
||||
#include <mach-omap2/prm-regbits-34xx.h>
|
||||
#include <mach-omap2/cm-regbits-34xx.h>
|
||||
#include <dspbridge/devdefs.h>
|
||||
|
|
|
@ -121,9 +121,13 @@ void dsp_clk_exit(void)
|
|||
for (i = 0; i < DM_TIMER_CLOCKS; i++)
|
||||
omap_dm_timer_free(timer[i]);
|
||||
|
||||
clk_unprepare(iva2_clk);
|
||||
clk_put(iva2_clk);
|
||||
clk_unprepare(ssi.sst_fck);
|
||||
clk_put(ssi.sst_fck);
|
||||
clk_unprepare(ssi.ssr_fck);
|
||||
clk_put(ssi.ssr_fck);
|
||||
clk_unprepare(ssi.ick);
|
||||
clk_put(ssi.ick);
|
||||
}
|
||||
|
||||
|
@ -145,14 +149,21 @@ void dsp_clk_init(void)
|
|||
iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
|
||||
if (IS_ERR(iva2_clk))
|
||||
dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk);
|
||||
else
|
||||
clk_prepare(iva2_clk);
|
||||
|
||||
ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
|
||||
ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
|
||||
ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
|
||||
|
||||
if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick))
|
||||
if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) {
|
||||
dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
|
||||
ssi.sst_fck, ssi.ssr_fck, ssi.ick);
|
||||
} else {
|
||||
clk_prepare(ssi.sst_fck);
|
||||
clk_prepare(ssi.ssr_fck);
|
||||
clk_prepare(ssi.ick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,11 +63,15 @@ int dsp_wdt_init(void)
|
|||
dsp_wdt.fclk = clk_get(NULL, "wdt3_fck");
|
||||
|
||||
if (!IS_ERR(dsp_wdt.fclk)) {
|
||||
clk_prepare(dsp_wdt.fclk);
|
||||
|
||||
dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
|
||||
if (IS_ERR(dsp_wdt.iclk)) {
|
||||
clk_put(dsp_wdt.fclk);
|
||||
dsp_wdt.fclk = NULL;
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
clk_prepare(dsp_wdt.iclk);
|
||||
}
|
||||
} else
|
||||
ret = -EFAULT;
|
||||
|
@ -95,10 +99,14 @@ void dsp_wdt_exit(void)
|
|||
free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt);
|
||||
tasklet_kill(&dsp_wdt.wdt3_tasklet);
|
||||
|
||||
if (dsp_wdt.fclk)
|
||||
if (dsp_wdt.fclk) {
|
||||
clk_unprepare(dsp_wdt.fclk);
|
||||
clk_put(dsp_wdt.fclk);
|
||||
if (dsp_wdt.iclk)
|
||||
}
|
||||
if (dsp_wdt.iclk) {
|
||||
clk_unprepare(dsp_wdt.iclk);
|
||||
clk_put(dsp_wdt.iclk);
|
||||
}
|
||||
|
||||
dsp_wdt.fclk = NULL;
|
||||
dsp_wdt.iclk = NULL;
|
||||
|
|
|
@ -162,11 +162,9 @@ static struct vme_driver pio2_driver = {
|
|||
|
||||
static int __init pio2_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (bus_num == 0) {
|
||||
pr_err("No cards, skipping registration\n");
|
||||
goto err_nocard;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (bus_num > PIO2_CARDS_MAX) {
|
||||
|
@ -176,15 +174,7 @@ static int __init pio2_init(void)
|
|||
}
|
||||
|
||||
/* Register the PIO2 driver */
|
||||
retval = vme_register_driver(&pio2_driver, bus_num);
|
||||
if (retval != 0)
|
||||
goto err_reg;
|
||||
|
||||
return retval;
|
||||
|
||||
err_reg:
|
||||
err_nocard:
|
||||
return retval;
|
||||
return vme_register_driver(&pio2_driver, bus_num);
|
||||
}
|
||||
|
||||
static int pio2_match(struct vme_dev *vdev)
|
||||
|
|
|
@ -638,8 +638,8 @@ int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
|||
}
|
||||
|
||||
|
||||
int prism2_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type,
|
||||
int mbm)
|
||||
int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm)
|
||||
{
|
||||
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
|
||||
wlandevice_t *wlandev = priv->wlandev;
|
||||
|
@ -665,7 +665,8 @@ exit:
|
|||
return err;
|
||||
}
|
||||
|
||||
int prism2_get_tx_power(struct wiphy *wiphy, int *dbm)
|
||||
int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
int *dbm)
|
||||
{
|
||||
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
|
||||
wlandevice_t *wlandev = priv->wlandev;
|
||||
|
|
|
@ -265,7 +265,7 @@ out_cleanup:
|
|||
static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
||||
int offset)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
size_t clen;
|
||||
unsigned long handle;
|
||||
struct page *page;
|
||||
|
@ -286,10 +286,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
goto out;
|
||||
}
|
||||
ret = zram_decompress_page(zram, uncmem, index);
|
||||
if (ret) {
|
||||
kfree(uncmem);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -302,16 +300,18 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
|
||||
user_mem = kmap_atomic(page);
|
||||
|
||||
if (is_partial_io(bvec))
|
||||
if (is_partial_io(bvec)) {
|
||||
memcpy(uncmem + offset, user_mem + bvec->bv_offset,
|
||||
bvec->bv_len);
|
||||
else
|
||||
kunmap_atomic(user_mem);
|
||||
user_mem = NULL;
|
||||
} else {
|
||||
uncmem = user_mem;
|
||||
}
|
||||
|
||||
if (page_zero_filled(uncmem)) {
|
||||
kunmap_atomic(user_mem);
|
||||
if (is_partial_io(bvec))
|
||||
kfree(uncmem);
|
||||
if (!is_partial_io(bvec))
|
||||
kunmap_atomic(user_mem);
|
||||
zram_stat_inc(&zram->stats.pages_zero);
|
||||
zram_set_flag(zram, index, ZRAM_ZERO);
|
||||
ret = 0;
|
||||
|
@ -321,9 +321,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
|
||||
zram->compress_workmem);
|
||||
|
||||
kunmap_atomic(user_mem);
|
||||
if (is_partial_io(bvec))
|
||||
kfree(uncmem);
|
||||
if (!is_partial_io(bvec)) {
|
||||
kunmap_atomic(user_mem);
|
||||
user_mem = NULL;
|
||||
uncmem = NULL;
|
||||
}
|
||||
|
||||
if (unlikely(ret != LZO_E_OK)) {
|
||||
pr_err("Compression failed! err=%d\n", ret);
|
||||
|
@ -332,8 +334,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
|
||||
if (unlikely(clen > max_zpage_size)) {
|
||||
zram_stat_inc(&zram->stats.bad_compress);
|
||||
src = uncmem;
|
||||
clen = PAGE_SIZE;
|
||||
src = NULL;
|
||||
if (is_partial_io(bvec))
|
||||
src = uncmem;
|
||||
}
|
||||
|
||||
handle = zs_malloc(zram->mem_pool, clen);
|
||||
|
@ -345,7 +349,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
}
|
||||
cmem = zs_map_object(zram->mem_pool, handle, ZS_MM_WO);
|
||||
|
||||
if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
|
||||
src = kmap_atomic(page);
|
||||
memcpy(cmem, src, clen);
|
||||
if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
|
||||
kunmap_atomic(src);
|
||||
|
||||
zs_unmap_object(zram->mem_pool, handle);
|
||||
|
||||
|
@ -358,9 +366,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|||
if (clen <= PAGE_SIZE / 2)
|
||||
zram_stat_inc(&zram->stats.good_compress);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (is_partial_io(bvec))
|
||||
kfree(uncmem);
|
||||
|
||||
if (ret)
|
||||
zram_stat64_inc(zram, &zram->stats.failed_writes);
|
||||
return ret;
|
||||
|
|
Loading…
Reference in New Issue