The first attempt doesn't work as the lazy-seq macro calls the body you give it when needed, from a zero-arity fn. So even though your code is in the tail position, it's in the tail position of another function. Gotta love macros. I recommend running (source lazy-seq) to grok it personally.
(defn osc-seq [val fns]
(cons val (lazy-seq (osc-seq ((first fns) val) (rest fns)))))
(defn osc [val & fns]
(osc-seq val (cycle fns)))
Your second attempt has problems due to repeatedly calling cycle and so (first fns) isn't a fn the second time - it's a seq.