firedtv: cleanups and minor fixes
[linux-2.6-block.git] / drivers / media / dvb / firesat / cmp.c
CommitLineData
df4846c3 1/*
612262a5 2 * FireDTV driver (formerly known as FireSAT)
df4846c3 3 *
612262a5
SR
4 * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
5 * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
df4846c3
HK
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 */
12
8ae83cdf 13#include <linux/device.h>
612262a5
SR
14#include <linux/kernel.h>
15#include <linux/mutex.h>
16#include <linux/types.h>
17
8ae83cdf
SR
18#include <asm/byteorder.h>
19
612262a5 20#include <ieee1394.h>
612262a5
SR
21#include <nodemgr.h>
22
c81c8b68 23#include "avc_api.h"
612262a5
SR
24#include "cmp.h"
25#include "firesat.h"
c81c8b68 26
8ae83cdf 27#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL
c81c8b68 28
612262a5
SR
29static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len)
30{
c81c8b68 31 int ret;
612262a5
SR
32
33 if (mutex_lock_interruptible(&firesat->avc_mutex))
c81c8b68
GKH
34 return -EINTR;
35
8ae83cdf
SR
36 ret = hpsb_node_read(firesat->ud->ne, addr, buf, len);
37 if (ret < 0)
38 dev_err(&firesat->ud->device, "CMP: read I/O error\n");
c81c8b68 39
612262a5 40 mutex_unlock(&firesat->avc_mutex);
c81c8b68
GKH
41 return ret;
42}
43
8ae83cdf
SR
44static int cmp_lock(struct firesat *firesat, void *data, u64 addr, __be32 arg,
45 int ext_tcode)
612262a5 46{
c81c8b68 47 int ret;
612262a5
SR
48
49 if (mutex_lock_interruptible(&firesat->avc_mutex))
c81c8b68
GKH
50 return -EINTR;
51
8ae83cdf
SR
52 ret = hpsb_node_lock(firesat->ud->ne, addr, ext_tcode, data,
53 (__force quadlet_t)arg);
54 if (ret < 0)
55 dev_err(&firesat->ud->device, "CMP: lock I/O error\n");
c81c8b68 56
612262a5 57 mutex_unlock(&firesat->avc_mutex);
c81c8b68
GKH
58 return ret;
59}
60
8ae83cdf
SR
61static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift)
62{
63 return (be32_to_cpu(opcr) >> shift) & mask;
64}
65
66static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift)
67{
68 *opcr &= ~cpu_to_be32(mask << shift);
69 *opcr |= cpu_to_be32((value & mask) << shift);
70}
c81c8b68 71
8ae83cdf
SR
72#define get_opcr_online(v) get_opcr((v), 0x1, 31)
73#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24)
74#define get_opcr_channel(v) get_opcr((v), 0x3f, 16)
c81c8b68 75
8ae83cdf
SR
76#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24)
77#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16)
78#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14)
79#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10)
c81c8b68 80
8ae83cdf
SR
81int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel)
82{
83 __be32 old_opcr, opcr;
84 u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
85 int attempts = 0;
86 int ret;
87
88 ret = cmp_read(firesat, &opcr, opcr_address, 4);
89 if (ret < 0)
90 return ret;
91
92repeat:
93 if (!get_opcr_online(opcr)) {
94 dev_err(&firesat->ud->device, "CMP: output offline\n");
95 return -EBUSY;
96 }
97
98 old_opcr = opcr;
99
100 if (get_opcr_p2p_connections(opcr)) {
101 if (get_opcr_channel(opcr) != channel) {
102 dev_err(&firesat->ud->device,
103 "CMP: cannot change channel\n");
104 return -EBUSY;
c81c8b68 105 }
8ae83cdf
SR
106 dev_info(&firesat->ud->device,
107 "CMP: overlaying existing connection\n");
108
109 /* We don't allocate isochronous resources. */
110 } else {
111 set_opcr_channel(&opcr, channel);
112 set_opcr_data_rate(&opcr, IEEE1394_SPEED_400);
113
114 /* FIXME: this is for the worst case - optimize */
115 set_opcr_overhead_id(&opcr, 0);
116
117 /* FIXME: allocate isochronous channel and bandwidth at IRM */
c81c8b68 118 }
8ae83cdf
SR
119
120 set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1);
121
122 ret = cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2);
123 if (ret < 0)
124 return ret;
125
126 if (old_opcr != opcr) {
127 /*
128 * FIXME: if old_opcr.P2P_Connections > 0,
129 * deallocate isochronous channel and bandwidth at IRM
130 */
131
132 if (++attempts < 6) /* arbitrary limit */
133 goto repeat;
134 return -EBUSY;
135 }
136
c81c8b68
GKH
137 return 0;
138}
139
8ae83cdf
SR
140void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel)
141{
142 __be32 old_opcr, opcr;
143 u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
144 int attempts = 0;
145
146 if (cmp_read(firesat, &opcr, opcr_address, 4) < 0)
147 return;
148
149repeat:
150 if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) ||
151 get_opcr_channel(opcr) != channel) {
152 dev_err(&firesat->ud->device, "CMP: no connection to break\n");
153 return;
154 }
155
156 old_opcr = opcr;
157 set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1);
c81c8b68 158
8ae83cdf
SR
159 if (cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2) < 0)
160 return;
c81c8b68 161
8ae83cdf
SR
162 if (old_opcr != opcr) {
163 /*
164 * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last
165 * owner, deallocate isochronous channel and bandwidth at IRM
166 */
c81c8b68 167
8ae83cdf
SR
168 if (++attempts < 6) /* arbitrary limit */
169 goto repeat;
170 }
c81c8b68 171}