/* Errands use the interrupts */
#define V_BLANK_FLIP 0
#define V_BLANK_NVSD 1
+#define V_BLANK_USER 2
#define V_PULSE2_FLIP 0
#define V_PULSE2_NVSD 1
bool tegra_dc_hpd(struct tegra_dc *dc);
+bool tegra_dc_has_vsync(struct tegra_dc *dc);
+void tegra_dc_vsync_enable(struct tegra_dc *dc);
+void tegra_dc_vsync_disable(struct tegra_dc *dc);
void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank);
int tegra_dc_wait_for_vsync(struct tegra_dc *dc);
void tegra_dc_blank(struct tegra_dc *dc, unsigned windows);
int tegra_dc_ext_restore(struct tegra_dc_ext *dc_ext);
int tegra_dc_ext_process_hotplug(int output);
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp);
int tegra_dc_ext_process_bandwidth_renegotiate(int output,
struct tegra_dc_bw_data *bw);
{
return 0;
}
+static inline
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp)
+{
+ return 0;
+}
#endif /* CONFIG_TEGRA_DC_EXTENSIONS */
#endif /* __MACH_TEGRA_DC_EXT_H */
tegra_dc_io_end(dc);
}
+void tegra_dc_hold_dc_out(struct tegra_dc *dc)
+{
+ if (1 == atomic_inc_return(&dc->holding)) {
+ tegra_dc_get(dc);
+ if (dc->out_ops && dc->out_ops->hold)
+ dc->out_ops->hold(dc);
+ atomic_inc(&dc->holding);
+ }
+}
+
+void tegra_dc_release_dc_out(struct tegra_dc *dc)
+{
+ if (0 == atomic_dec_return(&dc->holding)) {
+ if (dc->out_ops && dc->out_ops->release)
+ dc->out_ops->release(dc);
+ tegra_dc_put(dc);
+ }
+}
+
#define DUMP_REG(a) do { \
snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
#a, a, tegra_dc_readl(dc, a)); \
return ret;
}
+void tegra_dc_vsync_enable(struct tegra_dc *dc)
+{
+ mutex_lock(&dc->lock);
+ set_bit(V_BLANK_USER, &dc->vblank_ref_count);
+ tegra_dc_unmask_interrupt(dc, V_BLANK_INT);
+ mutex_unlock(&dc->lock);
+}
+
+void tegra_dc_vsync_disable(struct tegra_dc *dc)
+{
+ mutex_lock(&dc->lock);
+ clear_bit(V_BLANK_USER, &dc->vblank_ref_count);
+ if (!dc->vblank_ref_count)
+ tegra_dc_mask_interrupt(dc, V_BLANK_INT);
+ mutex_unlock(&dc->lock);
+}
+
static void tegra_dc_prism_update_backlight(struct tegra_dc *dc)
{
/* Do the actual brightness update outside of the mutex dc->lock */
}
#endif
-static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
+static void tegra_dc_process_vblank(struct tegra_dc *dc, ktime_t timestamp)
+{
+ if (test_bit(V_BLANK_USER, &dc->vblank_ref_count))
+ tegra_dc_ext_process_vblank(dc->ndev->id, timestamp);
+}
+
+static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status,
+ ktime_t timestamp)
{
/* pending user vblank, so wakeup */
- if ((status & (V_BLANK_INT | MSF_INT)) &&
- (dc->out->user_needs_vblank)) {
- dc->out->user_needs_vblank = false;
- complete(&dc->out->user_vblank_comp);
+ if (status & (V_BLANK_INT | MSF_INT)) {
+ if (dc->out->user_needs_vblank) {
+ dc->out->user_needs_vblank = false;
+ complete(&dc->out->user_vblank_comp);
+ }
+ tegra_dc_process_vblank(dc, timestamp);
}
if (status & V_BLANK_INT) {
#endif
}
-static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
+static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status,
+ ktime_t timestamp)
{
/* Schedule any additional bottom-half vblank actvities. */
if (status & V_BLANK_INT)
queue_work(system_freezable_wq, &dc->vblank_work);
+ if (status & (V_BLANK_INT | MSF_INT))
+ tegra_dc_process_vblank(dc, timestamp);
+
if (status & FRAME_END_INT) {
struct timespec tm = CURRENT_TIME;
dc->frame_end_timestamp = timespec_to_ns(&tm);
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
{
+ ktime_t timestamp = ktime_get();
struct tegra_dc *dc = ptr;
unsigned long status;
unsigned long underflow_mask;
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
- tegra_dc_one_shot_irq(dc, status);
+ tegra_dc_one_shot_irq(dc, status, timestamp);
else
- tegra_dc_continuous_irq(dc, status);
+ tegra_dc_continuous_irq(dc, status, timestamp);
if (dc->nvsr)
tegra_dc_nvsr_irq(dc->nvsr, status);
tegra_dc_get(dc);
+ if (atomic_read(&dc->holding)) {
+ /* Force release all refs but the last one */
+ atomic_set(&dc->holding, 1);
+ tegra_dc_release_dc_out(dc);
+ }
+
if (dc->out && dc->out->prepoweroff)
dc->out->prepoweroff();
atomic_t frame_end_ref;
bool mode_dirty;
+ atomic_t holding;
u32 reserved_bw;
u32 available_bw;
return tegra_dc_ext_queue_hotplug(&g_control, output);
}
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp)
+{
+ return tegra_dc_ext_queue_vblank(&g_control, output, timestamp);
+}
+
static int
get_output_properties(struct tegra_dc_ext_control_output_properties *properties)
{
};
static void tegra_dc_ext_unpin_window(struct tegra_dc_ext_win *win);
+static int tegra_dc_ext_set_vblank(struct tegra_dc_ext *ext, bool enable);
static inline s64 tegra_timespec_to_ns(const struct tegra_timespec *ts)
{
set_enable(ext, false);
+ /*
+ * Disable vblank requests
+ */
+ tegra_dc_ext_set_vblank(ext, false);
+
/*
* Flush the flip queue -- note that this must be called with dc->lock
* unlocked or else it will hang.
return err;
}
+static int tegra_dc_ext_set_vblank(struct tegra_dc_ext *ext, bool enable)
+{
+ struct tegra_dc *dc;
+
+ if (ext->vblank_enabled == enable)
+ return 0;
+
+ dc = ext->dc;
+
+ if (enable) {
+ tegra_dc_hold_dc_out(dc);
+ tegra_dc_vsync_enable(dc);
+ } else {
+ tegra_dc_vsync_disable(dc);
+ tegra_dc_release_dc_out(dc);
+ }
+
+ ext->vblank_enabled = enable;
+ return 0;
+}
+
static void (*flip_callback)(void);
static spinlock_t flip_callback_lock;
static bool init_tegra_dc_flip_callback_called;
#endif
}
+ case TEGRA_DC_EXT_SET_VBLANK:
+ {
+ struct tegra_dc_ext_set_vblank args;
+
+ if (copy_from_user(&args, user_arg, sizeof(args)))
+ return -EFAULT;
+
+ return tegra_dc_ext_set_vblank(user->ext, args.enable);
+ }
+
default:
return -EINVAL;
}
/*
* drivers/video/tegra/dc/ext/events.c
*
- * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION, All rights reserved.
*
* Author: Robert Morell <rmorell@nvidia.com>
*
struct list_head *cur;
int retval = 0;
+ WARN_ON(event->data_size > TEGRA_DC_EXT_EVENT_MAX_SZ);
+
mutex_lock(&control->lock);
list_for_each(cur, &control->users) {
struct tegra_dc_ext_control_user *user;
return 0;
}
+int tegra_dc_ext_queue_vblank(struct tegra_dc_ext_control *control, int output,
+ ktime_t timestamp)
+{
+ struct {
+ struct tegra_dc_ext_event event;
+ struct tegra_dc_ext_control_event_vblank vblank;
+ } __packed pack;
+
+ pack.event.type = TEGRA_DC_EXT_EVENT_VBLANK;
+ pack.event.data_size = sizeof(pack.vblank);
+
+ pack.vblank.handle = output;
+ pack.vblank.reserved = 0;
+ pack.vblank.timestamp_ns = ktime_to_ns(timestamp);
+
+ tegra_dc_ext_queue_event(control, &pack.event);
+
+ return 0;
+}
+
int tegra_dc_ext_queue_bandwidth_renegotiate(
struct tegra_dc_ext_control *control, int output,
struct tegra_dc_bw_data *data)
} cursor;
bool enabled;
+ bool vblank_enabled;
};
-#define TEGRA_DC_EXT_EVENT_MASK_ALL \
- (TEGRA_DC_EXT_EVENT_HOTPLUG | TEGRA_DC_EXT_EVENT_BANDWIDTH_INC | \
+#define TEGRA_DC_EXT_EVENT_MASK_ALL \
+ (TEGRA_DC_EXT_EVENT_HOTPLUG | \
+ TEGRA_DC_EXT_EVENT_VBLANK | \
+ TEGRA_DC_EXT_EVENT_BANDWIDTH_INC | \
TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC)
-#define TEGRA_DC_EXT_EVENT_MAX_SZ 8
+#define TEGRA_DC_EXT_EVENT_MAX_SZ 16
struct tegra_dc_ext_event_list {
struct tegra_dc_ext_event event;
extern int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *,
int output);
+extern int tegra_dc_ext_queue_vblank(struct tegra_dc_ext_control *,
+ int output, ktime_t timestamp);
extern int tegra_dc_ext_queue_bandwidth_renegotiate(
struct tegra_dc_ext_control *, int output,
struct tegra_dc_bw_data *data);
__u16 dirty_rect[4]; /* x,y,w,h for partial screen update. 0 ignores */
};
+/*
+ * vblank control - enable or disable vblank events
+ */
+
+struct tegra_dc_ext_set_vblank {
+ __u8 enable;
+ __u8 reserved[3]; /* unused - must be 0 */
+};
+
/*
* Cursor image format:
*
#define TEGRA_DC_EXT_FLIP3 \
_IOWR('D', 0x14, struct tegra_dc_ext_flip_3)
+#define TEGRA_DC_EXT_SET_VBLANK \
+ _IOW('D', 0x15, struct tegra_dc_ext_set_vblank)
+
enum tegra_dc_ext_control_output_type {
TEGRA_DC_EXT_DSI,
TEGRA_DC_EXT_LVDS,
char data[0];
};
-#define TEGRA_DC_EXT_EVENT_HOTPLUG 0x1
+/* Events types are bits in a mask */
+#define TEGRA_DC_EXT_EVENT_HOTPLUG (1 << 0)
struct tegra_dc_ext_control_event_hotplug {
__u32 handle;
};
-#define TEGRA_DC_EXT_EVENT_BANDWIDTH_INC 0x3
-#define TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC 0x4
+#define TEGRA_DC_EXT_EVENT_VBLANK (1 << 1)
+struct tegra_dc_ext_control_event_vblank {
+ __u32 handle;
+ __u32 reserved; /* unused */
+ __u64 timestamp_ns;
+};
+
+#define TEGRA_DC_EXT_EVENT_BANDWIDTH_INC (1 << 2)
+#define TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC (1 << 3)
struct tegra_dc_ext_control_event_bandwidth {
__u32 handle;
__u32 total_bw;