diff options
Diffstat (limited to 'drivers/media/dvb/firewire/cmp.c')
-rw-r--r-- | drivers/media/dvb/firewire/cmp.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/drivers/media/dvb/firewire/cmp.c b/drivers/media/dvb/firewire/cmp.c new file mode 100644 index 000000000000..821e033d8195 --- /dev/null +++ b/drivers/media/dvb/firewire/cmp.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * FireDTV driver (formerly known as FireSAT) | ||
3 | * | ||
4 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> | ||
5 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | ||
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 | |||
13 | #include <linux/device.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/mutex.h> | ||
16 | #include <linux/types.h> | ||
17 | |||
18 | #include <asm/byteorder.h> | ||
19 | |||
20 | #include <ieee1394.h> | ||
21 | #include <nodemgr.h> | ||
22 | |||
23 | #include "avc.h" | ||
24 | #include "cmp.h" | ||
25 | #include "firedtv.h" | ||
26 | |||
27 | #define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL | ||
28 | |||
29 | static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) | ||
30 | { | ||
31 | int ret; | ||
32 | |||
33 | if (mutex_lock_interruptible(&fdtv->avc_mutex)) | ||
34 | return -EINTR; | ||
35 | |||
36 | ret = hpsb_node_read(fdtv->ud->ne, addr, buf, len); | ||
37 | if (ret < 0) | ||
38 | dev_err(&fdtv->ud->device, "CMP: read I/O error\n"); | ||
39 | |||
40 | mutex_unlock(&fdtv->avc_mutex); | ||
41 | return ret; | ||
42 | } | ||
43 | |||
44 | static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg, | ||
45 | int ext_tcode) | ||
46 | { | ||
47 | int ret; | ||
48 | |||
49 | if (mutex_lock_interruptible(&fdtv->avc_mutex)) | ||
50 | return -EINTR; | ||
51 | |||
52 | ret = hpsb_node_lock(fdtv->ud->ne, addr, ext_tcode, data, | ||
53 | (__force quadlet_t)arg); | ||
54 | if (ret < 0) | ||
55 | dev_err(&fdtv->ud->device, "CMP: lock I/O error\n"); | ||
56 | |||
57 | mutex_unlock(&fdtv->avc_mutex); | ||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) | ||
62 | { | ||
63 | return (be32_to_cpu(opcr) >> shift) & mask; | ||
64 | } | ||
65 | |||
66 | static 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 | } | ||
71 | |||
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) | ||
75 | |||
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) | ||
80 | |||
81 | int cmp_establish_pp_connection(struct firedtv *fdtv, 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(fdtv, &opcr, opcr_address, 4); | ||
89 | if (ret < 0) | ||
90 | return ret; | ||
91 | |||
92 | repeat: | ||
93 | if (!get_opcr_online(opcr)) { | ||
94 | dev_err(&fdtv->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(&fdtv->ud->device, | ||
103 | "CMP: cannot change channel\n"); | ||
104 | return -EBUSY; | ||
105 | } | ||
106 | dev_info(&fdtv->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 */ | ||
118 | } | ||
119 | |||
120 | set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); | ||
121 | |||
122 | ret = cmp_lock(fdtv, &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 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | void cmp_break_pp_connection(struct firedtv *fdtv, 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(fdtv, &opcr, opcr_address, 4) < 0) | ||
147 | return; | ||
148 | |||
149 | repeat: | ||
150 | if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || | ||
151 | get_opcr_channel(opcr) != channel) { | ||
152 | dev_err(&fdtv->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); | ||
158 | |||
159 | if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr, 2) < 0) | ||
160 | return; | ||
161 | |||
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 | */ | ||
167 | |||
168 | if (++attempts < 6) /* arbitrary limit */ | ||
169 | goto repeat; | ||
170 | } | ||
171 | } | ||