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_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
select HID_SENSOR_IIO_COMMON
|
select HID_SENSOR_IIO_COMMON
|
||||||
|
select HID_SENSOR_IIO_TRIGGER
|
||||||
tristate "HID Accelerometers 3D"
|
tristate "HID Accelerometers 3D"
|
||||||
help
|
help
|
||||||
Say yes here to build support for the HID SENSOR
|
Say yes here to build support for the HID SENSOR
|
||||||
|
|
|
@ -411,7 +411,11 @@ static int ad7266_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg;
|
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 {
|
} else {
|
||||||
/* Use internal reference */
|
/* Use internal reference */
|
||||||
st->vref_uv = 2500000;
|
st->vref_uv = 2500000;
|
||||||
|
|
|
@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||||
*timestamp = pf->timestamp;
|
*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);
|
iio_trigger_notify_done(idev->trig);
|
||||||
|
|
||||||
|
|
|
@ -1605,19 +1605,20 @@ static int max1363_probe(struct i2c_client *client,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
error_free_irq:
|
error_free_irq:
|
||||||
free_irq(st->client->irq, indio_dev);
|
if (client->irq)
|
||||||
|
free_irq(st->client->irq, indio_dev);
|
||||||
error_uninit_buffer:
|
error_uninit_buffer:
|
||||||
iio_buffer_unregister(indio_dev);
|
iio_buffer_unregister(indio_dev);
|
||||||
error_cleanup_buffer:
|
error_cleanup_buffer:
|
||||||
max1363_buffer_cleanup(indio_dev);
|
max1363_buffer_cleanup(indio_dev);
|
||||||
error_free_available_scan_masks:
|
error_free_available_scan_masks:
|
||||||
kfree(indio_dev->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:
|
error_disable_reg:
|
||||||
regulator_disable(st->reg);
|
regulator_disable(st->reg);
|
||||||
error_put_reg:
|
error_put_reg:
|
||||||
regulator_put(st->reg);
|
regulator_put(st->reg);
|
||||||
|
error_unregister_map:
|
||||||
|
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||||
error_free_device:
|
error_free_device:
|
||||||
iio_device_free(indio_dev);
|
iio_device_free(indio_dev);
|
||||||
error_out:
|
error_out:
|
||||||
|
@ -1635,10 +1636,8 @@ static int max1363_remove(struct i2c_client *client)
|
||||||
iio_buffer_unregister(indio_dev);
|
iio_buffer_unregister(indio_dev);
|
||||||
max1363_buffer_cleanup(indio_dev);
|
max1363_buffer_cleanup(indio_dev);
|
||||||
kfree(indio_dev->available_scan_masks);
|
kfree(indio_dev->available_scan_masks);
|
||||||
if (!IS_ERR(st->reg)) {
|
regulator_disable(st->reg);
|
||||||
regulator_disable(st->reg);
|
regulator_put(st->reg);
|
||||||
regulator_put(st->reg);
|
|
||||||
}
|
|
||||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||||
iio_device_free(indio_dev);
|
iio_device_free(indio_dev);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common"
|
||||||
config HID_SENSOR_IIO_COMMON
|
config HID_SENSOR_IIO_COMMON
|
||||||
tristate "Common modules for all HID Sensor IIO drivers"
|
tristate "Common modules for all HID Sensor IIO drivers"
|
||||||
depends on HID_SENSOR_HUB
|
depends on HID_SENSOR_HUB
|
||||||
select IIO_TRIGGER if IIO_BUFFER
|
select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to build support for HID sensor to use
|
Say yes here to build support for HID sensor to use
|
||||||
HID sensor common processing for attributes and IIO triggers.
|
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
|
HID sensor drivers, this module contains processing for those
|
||||||
attributes.
|
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
|
config HID_SENSOR_ENUM_BASE_QUIRKS
|
||||||
bool "ENUM base quirks for HID Sensor IIO drivers"
|
bool "ENUM base quirks for HID Sensor IIO drivers"
|
||||||
depends on HID_SENSOR_IIO_COMMON
|
depends on HID_SENSOR_IIO_COMMON
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
|
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;
|
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 {
|
} else {
|
||||||
st->vref = st->chip_info->int_vref;
|
st->vref = st->chip_info->int_vref;
|
||||||
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
||||||
|
|
|
@ -226,7 +226,11 @@ static int ad5446_probe(struct device *dev, const char *name,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg;
|
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));
|
indio_dev = iio_device_alloc(sizeof(*st));
|
||||||
|
|
|
@ -296,7 +296,11 @@ static int ad5504_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg;
|
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);
|
spi_set_drvdata(spi, indio_dev);
|
||||||
|
|
|
@ -238,7 +238,11 @@ static int ad5624r_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg;
|
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);
|
spi_set_drvdata(spi, indio_dev);
|
||||||
|
|
|
@ -332,7 +332,11 @@ static int ad5686_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg;
|
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 =
|
st->chip_info =
|
||||||
|
|
|
@ -365,7 +365,11 @@ static int ad5791_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg_pos;
|
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");
|
st->reg_vss = regulator_get(&spi->dev, "vss");
|
||||||
|
@ -374,7 +378,11 @@ static int ad5791_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_put_reg_neg;
|
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;
|
st->pwr_down = true;
|
||||||
|
@ -428,6 +436,7 @@ error_put_reg_neg:
|
||||||
if (!IS_ERR(st->reg_vss))
|
if (!IS_ERR(st->reg_vss))
|
||||||
regulator_put(st->reg_vss);
|
regulator_put(st->reg_vss);
|
||||||
|
|
||||||
|
error_disable_reg_pos:
|
||||||
if (!IS_ERR(st->reg_vdd))
|
if (!IS_ERR(st->reg_vdd))
|
||||||
regulator_disable(st->reg_vdd);
|
regulator_disable(st->reg_vdd);
|
||||||
error_put_reg_pos:
|
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 ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
|
||||||
} while (r_cnt == 0);
|
} 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 */
|
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
|
||||||
st->r0_fract = do_div(tmp, st->r1_mod);
|
st->r0_fract = do_div(tmp, st->r1_mod);
|
||||||
st->r0_int = tmp;
|
st->r0_int = tmp;
|
||||||
|
|
|
@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
select HID_SENSOR_IIO_COMMON
|
select HID_SENSOR_IIO_COMMON
|
||||||
|
select HID_SENSOR_IIO_TRIGGER
|
||||||
tristate "HID Gyroscope 3D"
|
tristate "HID Gyroscope 3D"
|
||||||
help
|
help
|
||||||
Say yes here to build support for the HID SENSOR
|
Say yes here to build support for the HID SENSOR
|
||||||
|
|
|
@ -47,6 +47,7 @@ config HID_SENSOR_ALS
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
select HID_SENSOR_IIO_COMMON
|
select HID_SENSOR_IIO_COMMON
|
||||||
|
select HID_SENSOR_IIO_TRIGGER
|
||||||
tristate "HID ALS"
|
tristate "HID ALS"
|
||||||
help
|
help
|
||||||
Say yes here to build support for the HID SENSOR
|
Say yes here to build support for the HID SENSOR
|
||||||
|
|
|
@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
select HID_SENSOR_IIO_COMMON
|
select HID_SENSOR_IIO_COMMON
|
||||||
|
select HID_SENSOR_IIO_TRIGGER
|
||||||
tristate "HID Magenetometer 3D"
|
tristate "HID Magenetometer 3D"
|
||||||
help
|
help
|
||||||
Say yes here to build support for the HID SENSOR
|
Say yes here to build support for the HID SENSOR
|
||||||
|
|
|
@ -444,6 +444,7 @@ config COMEDI_ADQ12B
|
||||||
|
|
||||||
config COMEDI_NI_AT_A2150
|
config COMEDI_NI_AT_A2150
|
||||||
tristate "NI AT-A2150 ISA card support"
|
tristate "NI AT-A2150 ISA card support"
|
||||||
|
select COMEDI_FC
|
||||||
depends on VIRT_TO_BUS
|
depends on VIRT_TO_BUS
|
||||||
---help---
|
---help---
|
||||||
Enable support for National Instruments AT-A2150 cards
|
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) {
|
if (cmd == COMEDI_DEVCONFIG) {
|
||||||
rc = do_devconfig_ioctl(dev,
|
rc = do_devconfig_ioctl(dev,
|
||||||
(struct comedi_devconfig __user *)arg);
|
(struct comedi_devconfig __user *)arg);
|
||||||
|
if (rc == 0)
|
||||||
|
/* Evade comedi_auto_unconfig(). */
|
||||||
|
dev_file_info->hardware_device = NULL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -345,7 +345,7 @@ static int waveform_ai_cancel(struct comedi_device *dev,
|
||||||
struct waveform_private *devpriv = dev->private;
|
struct waveform_private *devpriv = dev->private;
|
||||||
|
|
||||||
devpriv->timer_running = 0;
|
devpriv->timer_running = 0;
|
||||||
del_timer(&devpriv->timer);
|
del_timer_sync(&devpriv->timer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -963,7 +963,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_625x_ao,
|
.ao_range_table = &range_ni_M_625x_ao,
|
||||||
.reg_type = ni_reg_625x,
|
.reg_type = ni_reg_625x,
|
||||||
.ao_unipolar = 0,
|
.ao_unipolar = 0,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 8,
|
.num_p0_dio_channels = 8,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -982,7 +982,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_625x_ao,
|
.ao_range_table = &range_ni_M_625x_ao,
|
||||||
.reg_type = ni_reg_625x,
|
.reg_type = ni_reg_625x,
|
||||||
.ao_unipolar = 0,
|
.ao_unipolar = 0,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 8,
|
.num_p0_dio_channels = 8,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1001,7 +1001,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_625x_ao,
|
.ao_range_table = &range_ni_M_625x_ao,
|
||||||
.reg_type = ni_reg_625x,
|
.reg_type = ni_reg_625x,
|
||||||
.ao_unipolar = 0,
|
.ao_unipolar = 0,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 8,
|
.num_p0_dio_channels = 8,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1037,7 +1037,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_625x_ao,
|
.ao_range_table = &range_ni_M_625x_ao,
|
||||||
.reg_type = ni_reg_625x,
|
.reg_type = ni_reg_625x,
|
||||||
.ao_unipolar = 0,
|
.ao_unipolar = 0,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 32,
|
.num_p0_dio_channels = 32,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1056,7 +1056,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_625x_ao,
|
.ao_range_table = &range_ni_M_625x_ao,
|
||||||
.reg_type = ni_reg_625x,
|
.reg_type = ni_reg_625x,
|
||||||
.ao_unipolar = 0,
|
.ao_unipolar = 0,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 32,
|
.num_p0_dio_channels = 32,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1092,7 +1092,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_628x_ao,
|
.ao_range_table = &range_ni_M_628x_ao,
|
||||||
.reg_type = ni_reg_628x,
|
.reg_type = ni_reg_628x,
|
||||||
.ao_unipolar = 1,
|
.ao_unipolar = 1,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 8,
|
.num_p0_dio_channels = 8,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1111,7 +1111,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_628x_ao,
|
.ao_range_table = &range_ni_M_628x_ao,
|
||||||
.reg_type = ni_reg_628x,
|
.reg_type = ni_reg_628x,
|
||||||
.ao_unipolar = 1,
|
.ao_unipolar = 1,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 8,
|
.num_p0_dio_channels = 8,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
@ -1147,7 +1147,7 @@ static const struct ni_board_struct ni_boards[] = {
|
||||||
.ao_range_table = &range_ni_M_628x_ao,
|
.ao_range_table = &range_ni_M_628x_ao,
|
||||||
.reg_type = ni_reg_628x,
|
.reg_type = ni_reg_628x,
|
||||||
.ao_unipolar = 1,
|
.ao_unipolar = 1,
|
||||||
.ao_speed = 357,
|
.ao_speed = 350,
|
||||||
.num_p0_dio_channels = 32,
|
.num_p0_dio_channels = 32,
|
||||||
.caldac = {caldac_none},
|
.caldac = {caldac_none},
|
||||||
.has_8255 = 0,
|
.has_8255 = 0,
|
||||||
|
|
|
@ -3,7 +3,9 @@ config FIREWIRE_SERIAL
|
||||||
depends on FIREWIRE
|
depends on FIREWIRE
|
||||||
help
|
help
|
||||||
This enables TTY over IEEE 1394, providing high-speed serial
|
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
|
To compile this driver as a module, say M here: the module will
|
||||||
be called firewire-serial.
|
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
|
1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR
|
||||||
- I/O is handled asynchronously which presents some issues when error
|
- I/O is handled asynchronously which presents some issues when error
|
||||||
conditions occur.
|
conditions occur.
|
||||||
|
@ -11,17 +11,9 @@ TODOs
|
||||||
-- Issues with firewire stack --
|
-- Issues with firewire stack --
|
||||||
1. This driver uses the same unregistered vendor id that the firewire core does
|
1. This driver uses the same unregistered vendor id that the firewire core does
|
||||||
(0xd00d1e). Perhaps this could be exposed as a define in
|
(0xd00d1e). Perhaps this could be exposed as a define in
|
||||||
firewire-constants.h?
|
firewire.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?
|
|
||||||
3. Maybe device_max_receive() and link_speed_to_max_payload() should be
|
3. Maybe device_max_receive() and link_speed_to_max_payload() should be
|
||||||
taken up by the firewire core?
|
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 --
|
-- Issues with TTY core --
|
||||||
1. Hack for alternate device name scheme
|
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 */
|
/* Returns the max receive packet size for the given card */
|
||||||
static inline int device_max_receive(struct fw_device *fw_device)
|
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)
|
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 inline int link_speed_to_max_payload(unsigned speed)
|
||||||
{
|
{
|
||||||
static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, };
|
static const int max_async[] = { 307, 614, 1229, 2458, };
|
||||||
BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200);
|
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)
|
if (limit_bw)
|
||||||
return max_async[speed];
|
return max_async[speed];
|
||||||
else
|
else
|
||||||
|
|
|
@ -27,8 +27,8 @@ config ADIS16130
|
||||||
config ADIS16260
|
config ADIS16260
|
||||||
tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver"
|
tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver"
|
||||||
depends on SPI
|
depends on SPI
|
||||||
select IIO_TRIGGER if IIO_BUFFER
|
select IIO_ADIS_LIB
|
||||||
select IIO_SW_RING if IIO_BUFFER
|
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to build support for Analog Devices ADIS16260 ADIS16265
|
Say yes here to build support for Analog Devices ADIS16260 ADIS16265
|
||||||
ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors.
|
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);
|
ret = imx_drm_encoder_register(imx_drm_encoder);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(imx_drm_encoder);
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_register;
|
goto err_register;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1104,7 +1104,9 @@ static int ipu_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_failed_irq;
|
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 */
|
/* Set MCU_T to divide MCU access window into 2 */
|
||||||
ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
|
ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
|
||||||
|
@ -1129,6 +1131,7 @@ failed_add_clients:
|
||||||
ipu_submodules_exit(ipu);
|
ipu_submodules_exit(ipu);
|
||||||
failed_submodules_init:
|
failed_submodules_init:
|
||||||
ipu_irq_exit(ipu);
|
ipu_irq_exit(ipu);
|
||||||
|
out_failed_reset:
|
||||||
out_failed_irq:
|
out_failed_irq:
|
||||||
clk_disable_unprepare(ipu->clk);
|
clk_disable_unprepare(ipu->clk);
|
||||||
failed_clk_get:
|
failed_clk_get:
|
||||||
|
|
|
@ -452,7 +452,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
|
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);
|
ret = PTR_ERR(ipu_crtc->ipu_ch);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
|
||||||
if (pdata->dp >= 0) {
|
if (pdata->dp >= 0) {
|
||||||
ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
|
ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
|
||||||
if (IS_ERR(ipu_crtc->dp)) {
|
if (IS_ERR(ipu_crtc->dp)) {
|
||||||
ret = PTR_ERR(ipu_crtc->ipu_ch);
|
ret = PTR_ERR(ipu_crtc->dp);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -548,6 +548,8 @@ static int ipu_drm_probe(struct platform_device *pdev)
|
||||||
ipu_crtc->dev = &pdev->dev;
|
ipu_crtc->dev = &pdev->dev;
|
||||||
|
|
||||||
ret = ipu_crtc_init(ipu_crtc, pdata);
|
ret = ipu_crtc_init(ipu_crtc, pdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, ipu_crtc);
|
platform_set_drvdata(pdev, ipu_crtc);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
ccflags-y := -Iinclude/drm -Werror
|
ccflags-y := -Iinclude/drm -Werror
|
||||||
omapdrm-y := omap_drv.o \
|
omapdrm-y := omap_drv.o \
|
||||||
|
omap_irq.o \
|
||||||
omap_debugfs.o \
|
omap_debugfs.o \
|
||||||
omap_crtc.o \
|
omap_crtc.o \
|
||||||
omap_plane.o \
|
omap_plane.o \
|
||||||
|
|
|
@ -17,9 +17,6 @@ TODO
|
||||||
. Revisit GEM sync object infrastructure.. TTM has some framework for this
|
. Revisit GEM sync object infrastructure.. TTM has some framework for this
|
||||||
already. Possibly this could be refactored out and made more common?
|
already. Possibly this could be refactored out and made more common?
|
||||||
There should be some way to do this with less wheel-reinvention.
|
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
|
. 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
|
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
|
CRTC's should be disabled, and on resume the LUT should be reprogrammed
|
||||||
|
|
|
@ -31,9 +31,10 @@
|
||||||
struct omap_connector {
|
struct omap_connector {
|
||||||
struct drm_connector base;
|
struct drm_connector base;
|
||||||
struct omap_dss_device *dssdev;
|
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)
|
struct omap_video_timings *timings)
|
||||||
{
|
{
|
||||||
mode->clock = timings->pixel_clock;
|
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;
|
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)
|
struct drm_display_mode *mode)
|
||||||
{
|
{
|
||||||
timings->pixel_clock = mode->clock;
|
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;
|
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_connector_dpms(struct drm_connector *connector, int mode)
|
static enum drm_connector_status omap_connector_detect(
|
||||||
{
|
|
||||||
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(
|
|
||||||
struct drm_connector *connector, bool force)
|
struct drm_connector *connector, bool force)
|
||||||
{
|
{
|
||||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
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_connector *omap_connector = to_omap_connector(connector);
|
||||||
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
||||||
|
|
||||||
dssdev->driver->disable(dssdev);
|
|
||||||
|
|
||||||
DBG("%s", omap_connector->dssdev->name);
|
DBG("%s", omap_connector->dssdev->name);
|
||||||
drm_sysfs_connector_remove(connector);
|
drm_sysfs_connector_remove(connector);
|
||||||
drm_connector_cleanup(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_encoder *omap_connector_attached_encoder(
|
||||||
struct drm_connector *connector)
|
struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct omap_connector *omap_connector = to_omap_connector(connector);
|
struct omap_connector *omap_connector = to_omap_connector(connector);
|
||||||
|
return omap_connector->encoder;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct drm_connector_funcs omap_connector_funcs = {
|
static const struct drm_connector_funcs omap_connector_funcs = {
|
||||||
.dpms = omap_connector_dpms,
|
.dpms = drm_helper_connector_dpms,
|
||||||
.detect = omap_connector_detect,
|
.detect = omap_connector_detect,
|
||||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||||
.destroy = omap_connector_destroy,
|
.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,
|
.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
|
/* flush an area of the framebuffer (in case of manual update display that
|
||||||
* is not automatically flushed)
|
* is not automatically flushed)
|
||||||
*/
|
*/
|
||||||
|
@ -344,7 +250,8 @@ void omap_connector_flush(struct drm_connector *connector,
|
||||||
|
|
||||||
/* initialize connector */
|
/* initialize connector */
|
||||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
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 drm_connector *connector = NULL;
|
||||||
struct omap_connector *omap_connector;
|
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->dssdev = dssdev;
|
||||||
|
omap_connector->encoder = encoder;
|
||||||
|
|
||||||
connector = &omap_connector->base;
|
connector = &omap_connector->base;
|
||||||
|
|
||||||
drm_connector_init(dev, connector, &omap_connector_funcs,
|
drm_connector_init(dev, connector, &omap_connector_funcs,
|
||||||
|
|
|
@ -28,19 +28,131 @@
|
||||||
struct omap_crtc {
|
struct omap_crtc {
|
||||||
struct drm_crtc base;
|
struct drm_crtc base;
|
||||||
struct drm_plane *plane;
|
struct drm_plane *plane;
|
||||||
|
|
||||||
const char *name;
|
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: */
|
/* if there is a pending flip, these will be non-null: */
|
||||||
struct drm_pending_vblank_event *event;
|
struct drm_pending_vblank_event *event;
|
||||||
struct drm_framebuffer *old_fb;
|
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)
|
static void omap_crtc_destroy(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct omap_crtc *omap_crtc = to_omap_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);
|
omap_crtc->plane->funcs->destroy(omap_crtc->plane);
|
||||||
drm_crtc_cleanup(crtc);
|
drm_crtc_cleanup(crtc);
|
||||||
|
|
||||||
kfree(omap_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_drm_private *priv = crtc->dev->dev_private;
|
||||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||||
|
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||||
int i;
|
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++) {
|
if (enabled != omap_crtc->enabled) {
|
||||||
struct drm_plane *plane = priv->planes[i];
|
omap_crtc->enabled = enabled;
|
||||||
if (plane->crtc == crtc)
|
omap_crtc->full_update = true;
|
||||||
WARN_ON(omap_plane_dpms(plane, mode));
|
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 drm_framebuffer *old_fb)
|
||||||
{
|
{
|
||||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
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,
|
0, 0, mode->hdisplay, mode->vdisplay,
|
||||||
x << 16, y << 16,
|
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)
|
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_plane *plane = omap_crtc->plane;
|
||||||
struct drm_display_mode *mode = &crtc->mode;
|
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,
|
0, 0, mode->hdisplay, mode->vdisplay,
|
||||||
x << 16, y << 16,
|
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)
|
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 void vblank_cb(void *arg)
|
||||||
{
|
{
|
||||||
static uint32_t sequence;
|
|
||||||
struct drm_crtc *crtc = arg;
|
struct drm_crtc *crtc = arg;
|
||||||
struct drm_device *dev = crtc->dev;
|
struct drm_device *dev = crtc->dev;
|
||||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||||
struct drm_pending_vblank_event *event = omap_crtc->event;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct timeval now;
|
|
||||||
|
|
||||||
WARN_ON(!event);
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
|
|
||||||
omap_crtc->event = NULL;
|
|
||||||
|
|
||||||
/* wakeup userspace */
|
/* wakeup userspace */
|
||||||
if (event) {
|
if (omap_crtc->event)
|
||||||
do_gettimeofday(&now);
|
drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->event_lock, flags);
|
omap_crtc->event = NULL;
|
||||||
/* TODO: we can't yet use the vblank time accounting,
|
omap_crtc->old_fb = NULL;
|
||||||
* because omapdss lower layer is the one that knows
|
|
||||||
* the irq # and registers the handler, which more or
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||||
* less defeats how drm_irq works.. for now just fake
|
}
|
||||||
* the sequence number and use gettimeofday..
|
|
||||||
*
|
static void page_flip_worker(struct work_struct *work)
|
||||||
event->event.sequence = drm_vblank_count_and_time(
|
{
|
||||||
dev, omap_crtc->id, &now);
|
struct omap_crtc *omap_crtc =
|
||||||
*/
|
container_of(work, struct omap_crtc, page_flip_work);
|
||||||
event->event.sequence = sequence++;
|
struct drm_crtc *crtc = &omap_crtc->base;
|
||||||
event->event.tv_sec = now.tv_sec;
|
struct drm_device *dev = crtc->dev;
|
||||||
event->event.tv_usec = now.tv_usec;
|
struct drm_display_mode *mode = &crtc->mode;
|
||||||
list_add_tail(&event->base.link,
|
struct drm_gem_object *bo;
|
||||||
&event->base.file_priv->event_list);
|
|
||||||
wake_up_interruptible(&event->base.file_priv->event_wait);
|
mutex_lock(&dev->mode_config.mutex);
|
||||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
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)
|
static void page_flip_cb(void *arg)
|
||||||
{
|
{
|
||||||
struct drm_crtc *crtc = arg;
|
struct drm_crtc *crtc = arg;
|
||||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||||
struct drm_framebuffer *old_fb = omap_crtc->old_fb;
|
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||||||
struct drm_gem_object *bo;
|
|
||||||
|
|
||||||
omap_crtc->old_fb = NULL;
|
/* avoid assumptions about what ctxt we are called from: */
|
||||||
|
queue_work(priv->wq, &omap_crtc->page_flip_work);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
|
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 omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||||
struct drm_gem_object *bo;
|
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");
|
dev_err(dev->dev, "already a pending flip\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
omap_crtc->old_fb = crtc->fb;
|
|
||||||
omap_crtc->event = event;
|
omap_crtc->event = event;
|
||||||
crtc->fb = fb;
|
crtc->fb = fb;
|
||||||
|
|
||||||
|
@ -234,14 +363,244 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
|
||||||
.load_lut = omap_crtc_load_lut,
|
.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 */
|
/* initialize crtc */
|
||||||
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
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 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) {
|
if (!omap_crtc) {
|
||||||
dev_err(dev->dev, "could not allocate CRTC\n");
|
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;
|
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->plane->crtc = crtc;
|
||||||
omap_crtc->name = ovl->name;
|
omap_crtc->name = channel_names[channel];
|
||||||
omap_crtc->id = id;
|
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_init(dev, crtc, &omap_crtc_funcs);
|
||||||
drm_crtc_helper_add(crtc, &omap_crtc_helper_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)
|
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_drm_private *priv = dev->dev_private;
|
||||||
struct omap_dss_device *dssdev = NULL;
|
struct omap_dss_device *dssdev = NULL;
|
||||||
int i, j;
|
int num_ovls = dss_feat_get_num_ovls();
|
||||||
unsigned int connected_connectors = 0;
|
int id;
|
||||||
|
|
||||||
drm_mode_config_init(dev);
|
drm_mode_config_init(dev);
|
||||||
|
|
||||||
if (pdata && pdata->kms_pdata) {
|
omap_drm_irq_install(dev);
|
||||||
kms_pdata = pdata->kms_pdata;
|
|
||||||
|
|
||||||
/* if platform data is provided by the board file, use it to
|
/*
|
||||||
* control which overlays, managers, and devices we own.
|
* Create private planes and CRTCs for the last NUM_CRTCs overlay
|
||||||
*/
|
* plus manager:
|
||||||
for (i = 0; i < kms_pdata->mgr_cnt; i++) {
|
*/
|
||||||
struct omap_overlay_manager *mgr =
|
for (id = 0; id < min(num_crtc, num_ovls); id++) {
|
||||||
omap_dss_get_overlay_manager(
|
struct drm_plane *plane;
|
||||||
kms_pdata->mgr_ids[i]);
|
struct drm_crtc *crtc;
|
||||||
create_encoder(dev, mgr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < kms_pdata->dev_cnt; i++) {
|
plane = omap_plane_init(dev, id, true);
|
||||||
struct omap_dss_device *dssdev =
|
crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
connected_connectors = detect_connectors(dev);
|
BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
|
||||||
|
priv->crtcs[id] = crtc;
|
||||||
|
priv->num_crtcs++;
|
||||||
|
|
||||||
j = 0;
|
priv->planes[id] = plane;
|
||||||
for (i = 0; i < kms_pdata->ovl_cnt; i++) {
|
priv->num_planes++;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for now keep the mapping of CRTCs and encoders static.. */
|
/*
|
||||||
for (i = 0; i < priv->num_encoders; i++) {
|
* Create normal planes for the remaining overlays:
|
||||||
struct drm_encoder *encoder = priv->encoders[i];
|
*/
|
||||||
struct omap_overlay_manager *mgr =
|
for (; id < num_ovls; id++) {
|
||||||
omap_encoder_get_manager(encoder);
|
struct drm_plane *plane = omap_plane_init(dev, id, false);
|
||||||
|
|
||||||
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
|
||||||
|
priv->planes[priv->num_planes++] = plane;
|
||||||
DBG("%s: possible_crtcs=%08x", mgr->name,
|
|
||||||
encoder->possible_crtcs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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_width = 32;
|
||||||
dev->mode_config.min_height = 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_file *file_priv)
|
||||||
{
|
{
|
||||||
struct drm_omap_gem_new *args = data;
|
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);
|
args->size.bytes, args->flags);
|
||||||
return omap_gem_new_handle(dev, file_priv, args->size,
|
return omap_gem_new_handle(dev, file_priv, args->size,
|
||||||
args->flags, &args->handle);
|
args->flags, &args->handle);
|
||||||
|
@ -510,7 +289,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data,
|
||||||
struct drm_gem_object *obj;
|
struct drm_gem_object *obj;
|
||||||
int ret = 0;
|
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);
|
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
|
@ -565,14 +344,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
|
||||||
|
|
||||||
dev->dev_private = priv;
|
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);
|
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&priv->obj_list);
|
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_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
|
||||||
dev->dev_private = NULL;
|
dev->dev_private = NULL;
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
omapdss_compat_uninit();
|
|
||||||
return ret;
|
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);
|
priv->fbdev = omap_fbdev_init(dev);
|
||||||
if (!priv->fbdev) {
|
if (!priv->fbdev) {
|
||||||
dev_warn(dev->dev, "omap_fbdev_init failed\n");
|
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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,8 +379,9 @@ static int dev_unload(struct drm_device *dev)
|
||||||
|
|
||||||
DBG("unload: dev=%p", dev);
|
DBG("unload: dev=%p", dev);
|
||||||
|
|
||||||
drm_vblank_cleanup(dev);
|
|
||||||
drm_kms_helper_poll_fini(dev);
|
drm_kms_helper_poll_fini(dev);
|
||||||
|
drm_vblank_cleanup(dev);
|
||||||
|
omap_drm_irq_uninstall(dev);
|
||||||
|
|
||||||
omap_fbdev_free(dev);
|
omap_fbdev_free(dev);
|
||||||
omap_modeset_free(dev);
|
omap_modeset_free(dev);
|
||||||
|
@ -619,8 +390,6 @@ static int dev_unload(struct drm_device *dev)
|
||||||
flush_workqueue(priv->wq);
|
flush_workqueue(priv->wq);
|
||||||
destroy_workqueue(priv->wq);
|
destroy_workqueue(priv->wq);
|
||||||
|
|
||||||
omapdss_compat_uninit();
|
|
||||||
|
|
||||||
kfree(dev->dev_private);
|
kfree(dev->dev_private);
|
||||||
dev->dev_private = NULL;
|
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);
|
ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
|
||||||
|
mutex_unlock(&dev->mode_config.mutex);
|
||||||
if (ret)
|
if (ret)
|
||||||
DBG("failed to restore crtc mode");
|
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);
|
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 = {
|
static const struct vm_operations_struct omap_gem_vm_ops = {
|
||||||
.fault = omap_gem_fault,
|
.fault = omap_gem_fault,
|
||||||
.open = drm_gem_vm_open,
|
.open = drm_gem_vm_open,
|
||||||
|
@ -778,12 +495,12 @@ static struct drm_driver omap_drm_driver = {
|
||||||
.preclose = dev_preclose,
|
.preclose = dev_preclose,
|
||||||
.postclose = dev_postclose,
|
.postclose = dev_postclose,
|
||||||
.get_vblank_counter = drm_vblank_count,
|
.get_vblank_counter = drm_vblank_count,
|
||||||
.enable_vblank = dev_enable_vblank,
|
.enable_vblank = omap_irq_enable_vblank,
|
||||||
.disable_vblank = dev_disable_vblank,
|
.disable_vblank = omap_irq_disable_vblank,
|
||||||
.irq_preinstall = dev_irq_preinstall,
|
.irq_preinstall = omap_irq_preinstall,
|
||||||
.irq_postinstall = dev_irq_postinstall,
|
.irq_postinstall = omap_irq_postinstall,
|
||||||
.irq_uninstall = dev_irq_uninstall,
|
.irq_uninstall = omap_irq_uninstall,
|
||||||
.irq_handler = dev_irq_handler,
|
.irq_handler = omap_irq_handler,
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
.debugfs_init = omap_debugfs_init,
|
.debugfs_init = omap_debugfs_init,
|
||||||
.debugfs_cleanup = omap_debugfs_cleanup,
|
.debugfs_cleanup = omap_debugfs_cleanup,
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/platform_data/omap_drm.h>
|
#include <linux/platform_data/omap_drm.h>
|
||||||
#include "omap_drm.h"
|
#include "omap_drm.h"
|
||||||
|
|
||||||
|
|
||||||
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||||
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
|
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
|
||||||
|
|
||||||
|
@ -39,6 +40,51 @@
|
||||||
*/
|
*/
|
||||||
#define MAX_MAPPERS 2
|
#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 {
|
struct omap_drm_private {
|
||||||
uint32_t omaprev;
|
uint32_t omaprev;
|
||||||
|
|
||||||
|
@ -58,6 +104,7 @@ struct omap_drm_private {
|
||||||
|
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
|
|
||||||
|
/* list of GEM objects: */
|
||||||
struct list_head obj_list;
|
struct list_head obj_list;
|
||||||
|
|
||||||
bool has_dmm;
|
bool has_dmm;
|
||||||
|
@ -65,6 +112,11 @@ struct omap_drm_private {
|
||||||
/* properties: */
|
/* properties: */
|
||||||
struct drm_property *rotation_prop;
|
struct drm_property *rotation_prop;
|
||||||
struct drm_property *zorder_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 */
|
/* 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_X 4
|
||||||
#define DRM_REFLECT_Y 5
|
#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
|
#ifdef CONFIG_DEBUG_FS
|
||||||
int omap_debugfs_init(struct drm_minor *minor);
|
int omap_debugfs_init(struct drm_minor *minor);
|
||||||
void omap_debugfs_cleanup(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);
|
void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||||||
#endif
|
#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);
|
struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
|
||||||
void omap_fbdev_free(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 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 drm_plane *omap_plane_init(struct drm_device *dev,
|
||||||
struct omap_overlay *ovl, unsigned int possible_crtcs,
|
int plane_id, bool private_plane);
|
||||||
bool priv);
|
|
||||||
int omap_plane_dpms(struct drm_plane *plane, int mode);
|
int omap_plane_dpms(struct drm_plane *plane, int mode);
|
||||||
int omap_plane_mode_set(struct drm_plane *plane,
|
int omap_plane_mode_set(struct drm_plane *plane,
|
||||||
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||||
int crtc_x, int crtc_y,
|
int crtc_x, int crtc_y,
|
||||||
unsigned int crtc_w, unsigned int crtc_h,
|
unsigned int crtc_w, unsigned int crtc_h,
|
||||||
uint32_t src_x, uint32_t src_y,
|
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 omap_plane_on_endwin(struct drm_plane *plane,
|
|
||||||
void (*fxn)(void *), void *arg);
|
void (*fxn)(void *), void *arg);
|
||||||
void omap_plane_install_properties(struct drm_plane *plane,
|
void omap_plane_install_properties(struct drm_plane *plane,
|
||||||
struct drm_mode_object *obj);
|
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_property *property, uint64_t val);
|
||||||
|
|
||||||
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
||||||
struct omap_overlay_manager *mgr);
|
struct omap_dss_device *dssdev);
|
||||||
struct omap_overlay_manager *omap_encoder_get_manager(
|
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 *encoder);
|
||||||
struct drm_encoder *omap_connector_attached_encoder(
|
struct drm_encoder *omap_connector_attached_encoder(
|
||||||
struct drm_connector *connector);
|
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,
|
void omap_connector_flush(struct drm_connector *connector,
|
||||||
int x, int y, int w, int h);
|
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 omap_framebuffer_get_formats(uint32_t *pixel_formats,
|
||||||
uint32_t max_formats, enum omap_color_mode supported_modes);
|
uint32_t max_formats, enum omap_color_mode supported_modes);
|
||||||
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
|
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);
|
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?
|
/* should these be made into common util helpers?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -22,37 +22,56 @@
|
||||||
#include "drm_crtc.h"
|
#include "drm_crtc.h"
|
||||||
#include "drm_crtc_helper.h"
|
#include "drm_crtc_helper.h"
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* encoder funcs
|
* encoder funcs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
|
#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 omap_encoder {
|
||||||
struct drm_encoder base;
|
struct drm_encoder base;
|
||||||
struct omap_overlay_manager *mgr;
|
struct omap_dss_device *dssdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void omap_encoder_destroy(struct drm_encoder *encoder)
|
static void omap_encoder_destroy(struct drm_encoder *encoder)
|
||||||
{
|
{
|
||||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||||
DBG("%s", omap_encoder->mgr->name);
|
|
||||||
drm_encoder_cleanup(encoder);
|
drm_encoder_cleanup(encoder);
|
||||||
kfree(omap_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)
|
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,
|
static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||||
const struct drm_display_mode *mode,
|
const struct drm_display_mode *mode,
|
||||||
struct drm_display_mode *adjusted_mode)
|
struct drm_display_mode *adjusted_mode)
|
||||||
{
|
{
|
||||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
|
||||||
DBG("%s", omap_encoder->mgr->name);
|
|
||||||
return true;
|
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 *mode,
|
||||||
struct drm_display_mode *adjusted_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)
|
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)
|
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 = {
|
static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
|
||||||
.dpms = omap_encoder_dpms,
|
.dpms = omap_encoder_dpms,
|
||||||
.mode_fixup = omap_encoder_mode_fixup,
|
.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,
|
.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);
|
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 */
|
/* initialize encoder */
|
||||||
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
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 drm_encoder *encoder = NULL;
|
||||||
struct omap_encoder *omap_encoder;
|
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);
|
omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
|
||||||
if (!omap_encoder) {
|
if (!omap_encoder) {
|
||||||
|
@ -133,33 +152,14 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
omap_encoder->mgr = mgr;
|
omap_encoder->dssdev = dssdev;
|
||||||
|
|
||||||
encoder = &omap_encoder->base;
|
encoder = &omap_encoder->base;
|
||||||
|
|
||||||
drm_encoder_init(dev, encoder, &omap_encoder_funcs,
|
drm_encoder_init(dev, encoder, &omap_encoder_funcs,
|
||||||
DRM_MODE_ENCODER_TMDS);
|
DRM_MODE_ENCODER_TMDS);
|
||||||
drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
|
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;
|
return encoder;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
|
@ -194,7 +194,7 @@ struct dma_buf_ops omap_dmabuf_ops = {
|
||||||
struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
|
struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
|
||||||
struct drm_gem_object *obj, int flags)
|
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,
|
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 omap_plane {
|
||||||
struct drm_plane base;
|
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_overlay_info info;
|
||||||
|
struct omap_drm_apply apply;
|
||||||
|
|
||||||
/* position/orientation of scanout within the fb: */
|
/* position/orientation of scanout within the fb: */
|
||||||
struct omap_drm_window win;
|
struct omap_drm_window win;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
/* last fb that we pinned: */
|
/* last fb that we pinned: */
|
||||||
struct drm_framebuffer *pinned_fb;
|
struct drm_framebuffer *pinned_fb;
|
||||||
|
@ -54,189 +56,15 @@ struct omap_plane {
|
||||||
uint32_t nformats;
|
uint32_t nformats;
|
||||||
uint32_t formats[32];
|
uint32_t formats[32];
|
||||||
|
|
||||||
/* for synchronizing access to unpins fifo */
|
struct omap_drm_irq error_irq;
|
||||||
struct mutex unpin_mutex;
|
|
||||||
|
|
||||||
/* 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 *);
|
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 */
|
// XXX maybe get rid of this and handle vblank in crtc too?
|
||||||
struct work_struct work;
|
struct callback apply_done_cb;
|
||||||
|
|
||||||
/* callback on next endwin irq */
|
|
||||||
struct callback endwin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 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)
|
static void unpin(void *arg, struct drm_gem_object *bo)
|
||||||
{
|
{
|
||||||
struct drm_plane *plane = arg;
|
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,
|
if (kfifo_put(&omap_plane->unpin_fifo,
|
||||||
(const struct drm_gem_object **)&bo)) {
|
(const struct drm_gem_object **)&bo)) {
|
||||||
omap_plane->pending_num_unpins++;
|
|
||||||
/* also hold a ref so it isn't free'd while pinned */
|
/* also hold a ref so it isn't free'd while pinned */
|
||||||
drm_gem_object_reference(bo);
|
drm_gem_object_reference(bo);
|
||||||
} else {
|
} else {
|
||||||
|
@ -264,13 +91,19 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
|
||||||
|
|
||||||
DBG("%p -> %p", pinned_fb, 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);
|
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) {
|
if (ret) {
|
||||||
dev_err(plane->dev->dev, "could not swap %p -> %p\n",
|
dev_err(plane->dev->dev, "could not swap %p -> %p\n",
|
||||||
omap_plane->pinned_fb, fb);
|
omap_plane->pinned_fb, fb);
|
||||||
|
if (fb)
|
||||||
|
drm_framebuffer_unreference(fb);
|
||||||
omap_plane->pinned_fb = NULL;
|
omap_plane->pinned_fb = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -281,31 +114,90 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update parameters that are dependent on the framebuffer dimensions and
|
static void omap_plane_pre_apply(struct omap_drm_apply *apply)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
struct omap_plane *omap_plane =
|
||||||
struct omap_overlay_info *info = &omap_plane->info;
|
container_of(apply, struct omap_plane, apply);
|
||||||
struct omap_drm_window *win = &omap_plane->win;
|
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;
|
int ret;
|
||||||
|
|
||||||
ret = update_pin(plane, plane->fb);
|
DBG("%s, enabled=%d", omap_plane->name, enabled);
|
||||||
if (ret) {
|
|
||||||
dev_err(plane->dev->dev,
|
/* if fb has changed, pin new fb: */
|
||||||
"could not pin fb: %d\n", ret);
|
update_pin(plane, enabled ? plane->fb : NULL);
|
||||||
omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
|
|
||||||
|
if (!enabled) {
|
||||||
|
dispc_ovl_enable(omap_plane->id, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel = omap_crtc_channel(crtc);
|
||||||
|
|
||||||
|
/* update scanout: */
|
||||||
omap_framebuffer_update_scanout(plane->fb, win, info);
|
omap_framebuffer_update_scanout(plane->fb, win, info);
|
||||||
|
|
||||||
DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
|
DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
|
||||||
win->src_x, win->src_y,
|
info->out_width, info->out_height,
|
||||||
(u32)info->paddr, (u32)info->p_uv_addr,
|
|
||||||
info->screen_width);
|
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,
|
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,
|
int crtc_x, int crtc_y,
|
||||||
unsigned int crtc_w, unsigned int crtc_h,
|
unsigned int crtc_w, unsigned int crtc_h,
|
||||||
uint32_t src_x, uint32_t src_y,
|
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_plane *omap_plane = to_omap_plane(plane);
|
||||||
struct omap_drm_window *win = &omap_plane->win;
|
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_w = src_w >> 16;
|
||||||
win->src_h = src_h >> 16;
|
win->src_h = src_h >> 16;
|
||||||
|
|
||||||
/* note: this is done after this fxn returns.. but if we need
|
if (fxn) {
|
||||||
* to do a commit/update_scanout, etc before this returns we
|
/* omap_crtc should ensure that a new page flip
|
||||||
* need the current value.
|
* 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->fb = fb;
|
||||||
plane->crtc = crtc;
|
plane->crtc = crtc;
|
||||||
|
|
||||||
update_scanout(plane);
|
return apply(plane);
|
||||||
update_manager(plane);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_plane_update(struct drm_plane *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_x, uint32_t src_y,
|
||||||
uint32_t src_w, uint32_t src_h)
|
uint32_t src_w, uint32_t src_h)
|
||||||
{
|
{
|
||||||
omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
|
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||||
src_x, src_y, src_w, src_h);
|
omap_plane->enabled = true;
|
||||||
return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
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)
|
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)
|
static void omap_plane_destroy(struct drm_plane *plane)
|
||||||
{
|
{
|
||||||
struct omap_plane *omap_plane = to_omap_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);
|
omap_plane_disable(plane);
|
||||||
drm_plane_cleanup(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);
|
kfifo_free(&omap_plane->unpin_fifo);
|
||||||
|
|
||||||
kfree(omap_plane);
|
kfree(omap_plane);
|
||||||
}
|
}
|
||||||
|
|
||||||
int omap_plane_dpms(struct drm_plane *plane, int mode)
|
int omap_plane_dpms(struct drm_plane *plane, int mode)
|
||||||
{
|
{
|
||||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||||
struct omap_overlay *ovl = omap_plane->ovl;
|
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||||
int r;
|
int ret = 0;
|
||||||
|
|
||||||
DBG("%s: %d", omap_plane->ovl->name, mode);
|
if (enabled != omap_plane->enabled) {
|
||||||
|
omap_plane->enabled = enabled;
|
||||||
if (mode == DRM_MODE_DPMS_ON) {
|
ret = apply(plane);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper to install properties which are common to planes and crtcs */
|
/* 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;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
if (property == priv->rotation_prop) {
|
if (property == priv->rotation_prop) {
|
||||||
struct omap_overlay *ovl = omap_plane->ovl;
|
DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
|
||||||
|
|
||||||
DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
|
|
||||||
omap_plane->win.rotation = val;
|
omap_plane->win.rotation = val;
|
||||||
|
ret = apply(plane);
|
||||||
if (ovl->is_enabled(ovl))
|
|
||||||
ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
|
||||||
else
|
|
||||||
ret = 0;
|
|
||||||
} else if (property == priv->zorder_prop) {
|
} else if (property == priv->zorder_prop) {
|
||||||
struct omap_overlay *ovl = omap_plane->ovl;
|
DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
|
||||||
|
|
||||||
DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
|
|
||||||
omap_plane->info.zorder = val;
|
omap_plane->info.zorder = val;
|
||||||
|
ret = apply(plane);
|
||||||
if (ovl->is_enabled(ovl))
|
|
||||||
ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
|
|
||||||
else
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -485,20 +356,38 @@ static const struct drm_plane_funcs omap_plane_funcs = {
|
||||||
.set_property = omap_plane_set_property,
|
.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 */
|
/* initialize plane */
|
||||||
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||||||
struct omap_overlay *ovl, unsigned int possible_crtcs,
|
int id, bool private_plane)
|
||||||
bool priv)
|
|
||||||
{
|
{
|
||||||
|
struct omap_drm_private *priv = dev->dev_private;
|
||||||
struct drm_plane *plane = NULL;
|
struct drm_plane *plane = NULL;
|
||||||
struct omap_plane *omap_plane;
|
struct omap_plane *omap_plane;
|
||||||
|
struct omap_overlay_info *info;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
|
DBG("%s: priv=%d", plane_names[id], private_plane);
|
||||||
possible_crtcs, priv);
|
|
||||||
|
|
||||||
/* friendly reminder to update table for future hw: */
|
|
||||||
WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
|
|
||||||
|
|
||||||
omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
|
omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
|
||||||
if (!omap_plane) {
|
if (!omap_plane) {
|
||||||
|
@ -506,47 +395,50 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&omap_plane->unpin_mutex);
|
|
||||||
|
|
||||||
ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
|
ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev->dev, "could not allocate unpin FIFO\n");
|
dev_err(dev->dev, "could not allocate unpin FIFO\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&omap_plane->work, unpin_worker);
|
|
||||||
|
|
||||||
omap_plane->nformats = omap_framebuffer_get_formats(
|
omap_plane->nformats = omap_framebuffer_get_formats(
|
||||||
omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
|
omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
|
||||||
ovl->supported_modes);
|
dss_feat_get_supported_color_modes(id));
|
||||||
omap_plane->ovl = ovl;
|
omap_plane->id = id;
|
||||||
|
omap_plane->name = plane_names[id];
|
||||||
|
|
||||||
plane = &omap_plane->base;
|
plane = &omap_plane->base;
|
||||||
|
|
||||||
drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
|
omap_plane->apply.pre_apply = omap_plane_pre_apply;
|
||||||
omap_plane->formats, omap_plane->nformats, priv);
|
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);
|
omap_plane_install_properties(plane, &plane->base);
|
||||||
|
|
||||||
/* get our starting configuration, set defaults for parameters
|
/* get our starting configuration, set defaults for parameters
|
||||||
* we don't currently use, etc:
|
* we don't currently use, etc:
|
||||||
*/
|
*/
|
||||||
ovl->get_overlay_info(ovl, &omap_plane->info);
|
info = &omap_plane->info;
|
||||||
omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
|
info->rotation_type = OMAP_DSS_ROT_DMA;
|
||||||
omap_plane->info.rotation = OMAP_DSS_ROT_0;
|
info->rotation = OMAP_DSS_ROT_0;
|
||||||
omap_plane->info.global_alpha = 0xff;
|
info->global_alpha = 0xff;
|
||||||
omap_plane->info.mirror = 0;
|
info->mirror = 0;
|
||||||
|
|
||||||
/* Set defaults depending on whether we are a CRTC or overlay
|
/* Set defaults depending on whether we are a CRTC or overlay
|
||||||
* layer.
|
* layer.
|
||||||
* TODO add ioctl to give userspace an API to change this.. this
|
* TODO add ioctl to give userspace an API to change this.. this
|
||||||
* will come in a subsequent patch.
|
* will come in a subsequent patch.
|
||||||
*/
|
*/
|
||||||
if (priv)
|
if (private_plane)
|
||||||
omap_plane->info.zorder = 0;
|
omap_plane->info.zorder = 0;
|
||||||
else
|
else
|
||||||
omap_plane->info.zorder = ovl->id;
|
omap_plane->info.zorder = id;
|
||||||
|
|
||||||
update_manager(plane);
|
|
||||||
|
|
||||||
return plane;
|
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),
|
dma_tmp = pci_map_single(pdev, buf, bufsize * sizeof(u8),
|
||||||
PCI_DMA_FROMDEVICE);
|
PCI_DMA_FROMDEVICE);
|
||||||
|
if (pci_dma_mapping_error(pdev, dma_tmp))
|
||||||
|
return -1;
|
||||||
if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp,
|
if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp,
|
||||||
&(priv->rxbufferhead))) {
|
&(priv->rxbufferhead))) {
|
||||||
DMESGE("Unable to allocate mem RX buf");
|
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,
|
pTxFwInfo->TxRate,
|
||||||
cb_desc);
|
cb_desc);
|
||||||
|
|
||||||
|
if (pci_dma_mapping_error(priv->pdev, mapping))
|
||||||
|
RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
|
||||||
if (cb_desc->bAMPDUEnable) {
|
if (cb_desc->bAMPDUEnable) {
|
||||||
pTxFwInfo->AllowAggregation = 1;
|
pTxFwInfo->AllowAggregation = 1;
|
||||||
pTxFwInfo->RxMF = cb_desc->ampdu_factor;
|
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,
|
dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
|
||||||
PCI_DMA_TODEVICE);
|
PCI_DMA_TODEVICE);
|
||||||
|
|
||||||
|
if (pci_dma_mapping_error(priv->pdev, mapping))
|
||||||
|
RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
|
||||||
memset(entry, 0, 12);
|
memset(entry, 0, 12);
|
||||||
entry->LINIP = cb_desc->bLastIniPkt;
|
entry->LINIP = cb_desc->bLastIniPkt;
|
||||||
entry->FirstSeg = 1;
|
entry->FirstSeg = 1;
|
||||||
|
|
|
@ -2104,7 +2104,10 @@ static short rtl8192_alloc_rx_desc_ring(struct net_device *dev)
|
||||||
skb_tail_pointer_rsl(skb),
|
skb_tail_pointer_rsl(skb),
|
||||||
priv->rxbuffersize,
|
priv->rxbuffersize,
|
||||||
PCI_DMA_FROMDEVICE);
|
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->BufferAddress = cpu_to_le32(*mapping);
|
||||||
|
|
||||||
entry->Length = priv->rxbuffersize;
|
entry->Length = priv->rxbuffersize;
|
||||||
|
@ -2397,7 +2400,11 @@ static void rtl8192_rx_normal(struct net_device *dev)
|
||||||
skb_tail_pointer_rsl(skb),
|
skb_tail_pointer_rsl(skb),
|
||||||
priv->rxbuffersize,
|
priv->rxbuffersize,
|
||||||
PCI_DMA_FROMDEVICE);
|
PCI_DMA_FROMDEVICE);
|
||||||
|
if (pci_dma_mapping_error(priv->pdev,
|
||||||
|
*((dma_addr_t *)skb->cb))) {
|
||||||
|
dev_kfree_skb_any(skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
pdesc->BufferAddress = cpu_to_le32(*((dma_addr_t *)skb->cb));
|
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 */
|
{USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */
|
||||||
/* Belkin */
|
/* Belkin */
|
||||||
{USB_DEVICE(0x050D, 0x945A)},
|
{USB_DEVICE(0x050D, 0x945A)},
|
||||||
|
/* ISY IWL - Belkin clone */
|
||||||
|
{USB_DEVICE(0x050D, 0x11F1)},
|
||||||
/* Corega */
|
/* Corega */
|
||||||
{USB_DEVICE(0x07AA, 0x0047)},
|
{USB_DEVICE(0x07AA, 0x0047)},
|
||||||
/* D-Link */
|
/* D-Link */
|
||||||
|
|
|
@ -2,6 +2,7 @@ config SB105X
|
||||||
tristate "SystemBase PCI Multiport UART"
|
tristate "SystemBase PCI Multiport UART"
|
||||||
select SERIAL_CORE
|
select SERIAL_CORE
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
depends on X86
|
||||||
help
|
help
|
||||||
A driver for the SystemBase Multi-2/PCI serial card
|
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);
|
sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#ifdef CONFIG_PARPORT
|
||||||
case PCI_DEVICE_ID_MP2S1P :
|
case PCI_DEVICE_ID_MP2S1P :
|
||||||
sbdev->nr_ports = 2;
|
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 */
|
/* 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);
|
parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name);
|
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);
|
mutex_lock(&spk_mutex);
|
||||||
/* First, check if we already have it loaded. */
|
/* 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)
|
if (strcmp(synths[i]->name, synth_name) == 0)
|
||||||
synth = synths[i];
|
synth = synths[i];
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ int synth_add(struct spk_synth *in_synth)
|
||||||
int i;
|
int i;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
mutex_lock(&spk_mutex);
|
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 */
|
/* synth_remove() is responsible for rotating the array down */
|
||||||
if (in_synth == synths[i]) {
|
if (in_synth == synths[i]) {
|
||||||
mutex_unlock(&spk_mutex);
|
mutex_unlock(&spk_mutex);
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
* driver should read or write to PRM/CM registers directly; they
|
* driver should read or write to PRM/CM registers directly; they
|
||||||
* should rely on OMAP core code to do this.
|
* 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/prm-regbits-34xx.h>
|
||||||
#include <mach-omap2/cm-regbits-34xx.h>
|
#include <mach-omap2/cm-regbits-34xx.h>
|
||||||
#include <dspbridge/devdefs.h>
|
#include <dspbridge/devdefs.h>
|
||||||
|
|
|
@ -121,9 +121,13 @@ void dsp_clk_exit(void)
|
||||||
for (i = 0; i < DM_TIMER_CLOCKS; i++)
|
for (i = 0; i < DM_TIMER_CLOCKS; i++)
|
||||||
omap_dm_timer_free(timer[i]);
|
omap_dm_timer_free(timer[i]);
|
||||||
|
|
||||||
|
clk_unprepare(iva2_clk);
|
||||||
clk_put(iva2_clk);
|
clk_put(iva2_clk);
|
||||||
|
clk_unprepare(ssi.sst_fck);
|
||||||
clk_put(ssi.sst_fck);
|
clk_put(ssi.sst_fck);
|
||||||
|
clk_unprepare(ssi.ssr_fck);
|
||||||
clk_put(ssi.ssr_fck);
|
clk_put(ssi.ssr_fck);
|
||||||
|
clk_unprepare(ssi.ick);
|
||||||
clk_put(ssi.ick);
|
clk_put(ssi.ick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,14 +149,21 @@ void dsp_clk_init(void)
|
||||||
iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
|
iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
|
||||||
if (IS_ERR(iva2_clk))
|
if (IS_ERR(iva2_clk))
|
||||||
dev_err(bridge, "failed to get iva2 clock %p\n", 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.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
|
||||||
ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
|
ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
|
||||||
ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
|
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",
|
dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
|
||||||
ssi.sst_fck, ssi.ssr_fck, ssi.ick);
|
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");
|
dsp_wdt.fclk = clk_get(NULL, "wdt3_fck");
|
||||||
|
|
||||||
if (!IS_ERR(dsp_wdt.fclk)) {
|
if (!IS_ERR(dsp_wdt.fclk)) {
|
||||||
|
clk_prepare(dsp_wdt.fclk);
|
||||||
|
|
||||||
dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
|
dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
|
||||||
if (IS_ERR(dsp_wdt.iclk)) {
|
if (IS_ERR(dsp_wdt.iclk)) {
|
||||||
clk_put(dsp_wdt.fclk);
|
clk_put(dsp_wdt.fclk);
|
||||||
dsp_wdt.fclk = NULL;
|
dsp_wdt.fclk = NULL;
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
|
} else {
|
||||||
|
clk_prepare(dsp_wdt.iclk);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
|
@ -95,10 +99,14 @@ void dsp_wdt_exit(void)
|
||||||
free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt);
|
free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt);
|
||||||
tasklet_kill(&dsp_wdt.wdt3_tasklet);
|
tasklet_kill(&dsp_wdt.wdt3_tasklet);
|
||||||
|
|
||||||
if (dsp_wdt.fclk)
|
if (dsp_wdt.fclk) {
|
||||||
|
clk_unprepare(dsp_wdt.fclk);
|
||||||
clk_put(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);
|
clk_put(dsp_wdt.iclk);
|
||||||
|
}
|
||||||
|
|
||||||
dsp_wdt.fclk = NULL;
|
dsp_wdt.fclk = NULL;
|
||||||
dsp_wdt.iclk = NULL;
|
dsp_wdt.iclk = NULL;
|
||||||
|
|
|
@ -162,11 +162,9 @@ static struct vme_driver pio2_driver = {
|
||||||
|
|
||||||
static int __init pio2_init(void)
|
static int __init pio2_init(void)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
if (bus_num == 0) {
|
if (bus_num == 0) {
|
||||||
pr_err("No cards, skipping registration\n");
|
pr_err("No cards, skipping registration\n");
|
||||||
goto err_nocard;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bus_num > PIO2_CARDS_MAX) {
|
if (bus_num > PIO2_CARDS_MAX) {
|
||||||
|
@ -176,15 +174,7 @@ static int __init pio2_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register the PIO2 driver */
|
/* Register the PIO2 driver */
|
||||||
retval = vme_register_driver(&pio2_driver, bus_num);
|
return vme_register_driver(&pio2_driver, bus_num);
|
||||||
if (retval != 0)
|
|
||||||
goto err_reg;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
err_reg:
|
|
||||||
err_nocard:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pio2_match(struct vme_dev *vdev)
|
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 prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||||
int mbm)
|
enum nl80211_tx_power_setting type, int mbm)
|
||||||
{
|
{
|
||||||
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
|
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
|
||||||
wlandevice_t *wlandev = priv->wlandev;
|
wlandevice_t *wlandev = priv->wlandev;
|
||||||
|
@ -665,7 +665,8 @@ exit:
|
||||||
return err;
|
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);
|
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
|
||||||
wlandevice_t *wlandev = priv->wlandev;
|
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,
|
static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
||||||
int offset)
|
int offset)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret = 0;
|
||||||
size_t clen;
|
size_t clen;
|
||||||
unsigned long handle;
|
unsigned long handle;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
@ -286,10 +286,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = zram_decompress_page(zram, uncmem, index);
|
ret = zram_decompress_page(zram, uncmem, index);
|
||||||
if (ret) {
|
if (ret)
|
||||||
kfree(uncmem);
|
|
||||||
goto out;
|
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);
|
user_mem = kmap_atomic(page);
|
||||||
|
|
||||||
if (is_partial_io(bvec))
|
if (is_partial_io(bvec)) {
|
||||||
memcpy(uncmem + offset, user_mem + bvec->bv_offset,
|
memcpy(uncmem + offset, user_mem + bvec->bv_offset,
|
||||||
bvec->bv_len);
|
bvec->bv_len);
|
||||||
else
|
kunmap_atomic(user_mem);
|
||||||
|
user_mem = NULL;
|
||||||
|
} else {
|
||||||
uncmem = user_mem;
|
uncmem = user_mem;
|
||||||
|
}
|
||||||
|
|
||||||
if (page_zero_filled(uncmem)) {
|
if (page_zero_filled(uncmem)) {
|
||||||
kunmap_atomic(user_mem);
|
if (!is_partial_io(bvec))
|
||||||
if (is_partial_io(bvec))
|
kunmap_atomic(user_mem);
|
||||||
kfree(uncmem);
|
|
||||||
zram_stat_inc(&zram->stats.pages_zero);
|
zram_stat_inc(&zram->stats.pages_zero);
|
||||||
zram_set_flag(zram, index, ZRAM_ZERO);
|
zram_set_flag(zram, index, ZRAM_ZERO);
|
||||||
ret = 0;
|
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,
|
ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
|
||||||
zram->compress_workmem);
|
zram->compress_workmem);
|
||||||
|
|
||||||
kunmap_atomic(user_mem);
|
if (!is_partial_io(bvec)) {
|
||||||
if (is_partial_io(bvec))
|
kunmap_atomic(user_mem);
|
||||||
kfree(uncmem);
|
user_mem = NULL;
|
||||||
|
uncmem = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(ret != LZO_E_OK)) {
|
if (unlikely(ret != LZO_E_OK)) {
|
||||||
pr_err("Compression failed! err=%d\n", ret);
|
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)) {
|
if (unlikely(clen > max_zpage_size)) {
|
||||||
zram_stat_inc(&zram->stats.bad_compress);
|
zram_stat_inc(&zram->stats.bad_compress);
|
||||||
src = uncmem;
|
|
||||||
clen = PAGE_SIZE;
|
clen = PAGE_SIZE;
|
||||||
|
src = NULL;
|
||||||
|
if (is_partial_io(bvec))
|
||||||
|
src = uncmem;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = zs_malloc(zram->mem_pool, clen);
|
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);
|
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);
|
memcpy(cmem, src, clen);
|
||||||
|
if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
|
||||||
|
kunmap_atomic(src);
|
||||||
|
|
||||||
zs_unmap_object(zram->mem_pool, handle);
|
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)
|
if (clen <= PAGE_SIZE / 2)
|
||||||
zram_stat_inc(&zram->stats.good_compress);
|
zram_stat_inc(&zram->stats.good_compress);
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
if (is_partial_io(bvec))
|
||||||
|
kfree(uncmem);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
zram_stat64_inc(zram, &zram->stats.failed_writes);
|
zram_stat64_inc(zram, &zram->stats.failed_writes);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Reference in New Issue